diff options
| author | Přemysl Janouch <p.janouch@gmail.com> | 2016-10-12 13:11:17 +0200 | 
|---|---|---|
| committer | Přemysl Janouch <p.janouch@gmail.com> | 2016-10-12 15:10:32 +0200 | 
| commit | 7b79dc3e57bfa1c4bc5ddebeb67a48f5fd88318c (patch) | |
| tree | 50dfaaaf8f8499b4547354b5caf8619aa456fd02 | |
| parent | 4542bdd239f3f3d89fb9f60a0198ab76452e9c4e (diff) | |
| download | nncmpp-7b79dc3e57bfa1c4bc5ddebeb67a48f5fd88318c.tar.gz nncmpp-7b79dc3e57bfa1c4bc5ddebeb67a48f5fd88318c.tar.xz nncmpp-7b79dc3e57bfa1c4bc5ddebeb67a48f5fd88318c.zip | |
Try to optimize playlists
I'm not entirely sure about this.
| -rw-r--r-- | nncmpp.c | 233 | 
1 files changed, 137 insertions, 96 deletions
| @@ -407,6 +407,119 @@ poller_curl_remove (struct poller_curl *self, CURL *easy, struct error **e)  	return true;  } +// --- Compact map ------------------------------------------------------------- + +// MPD provides us with a hefty amount of little key-value maps.  The overhead +// of str_map for such constant (string -> string) maps is too high and it's +// much better to serialize them (mainly cache locality and memory efficiency). +// +// This isn't intended to be reusable and has case insensitivity built-in. + +typedef uint8_t *compact_map_t;         ///< Compacted (string -> string) map + +static compact_map_t +compact_map (struct str_map *map) +{ +	struct str s; +	str_init (&s); +	struct str_map_iter iter; +	str_map_iter_init (&iter, map); + +	char *value; +	static const size_t zero = 0, alignment = sizeof zero; +	while ((value = str_map_iter_next (&iter))) +	{ +		size_t entry_len = iter.link->key_length + 1 + strlen (value) + 1; +		size_t padding_len = (alignment - entry_len % alignment) % alignment; +		entry_len += padding_len; + +		str_append_data (&s, &entry_len, sizeof entry_len); +		str_append_printf (&s, "%s%c%s%c", iter.link->key, 0, value, 0); +		str_append_data (&s, &zero, padding_len); +	} +	str_append_data (&s, &zero, sizeof zero); +	return (compact_map_t) str_steal (&s); +} + +static char * +compact_map_find (compact_map_t data, const char *needle) +{ +	size_t entry_len; +	while ((entry_len = *(size_t *) data)) +	{ +		data += sizeof entry_len; +		if (!strcasecmp_ascii (needle, (const char *) data)) +			return (char *) data + strlen (needle) + 1; +		data += entry_len; +	} +	return NULL; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +struct item_list +{ +	compact_map_t *items;               ///< Compacted (string -> string) maps +	size_t len;                         ///< Length +	size_t alloc;                       ///< Allocated items +}; + +static void +item_list_init (struct item_list *self) +{ +	memset (self, 0, sizeof *self); +	self->items = xcalloc (sizeof *self->items, (self->alloc = 16)); +} + +static void +item_list_free (struct item_list *self) +{ +	for (size_t i = 0; i < self->len; i++) +		free (self->items[i]); +	free (self->items); +} + +static bool +item_list_set (struct item_list *self, int i, struct str_map *item) +{ +	if (i < 0 || (size_t) i >= self->len) +		return false; + +	free (self->items[i]); +	self->items[i] = compact_map (item); +	return true; +} + +static compact_map_t +item_list_get (struct item_list *self, int i) +{ +	if (i < 0 || (size_t) i >= self->len || !self->items[i]) +		return false; +	return self->items[i]; +} + +static void +item_list_resize (struct item_list *self, size_t len) +{ +	// Make the allocated array big enough but not too large +	size_t new_alloc = self->alloc; +	while (new_alloc < len) +		new_alloc <<= 1; +	while ((new_alloc >> 1) >= len +		&& (new_alloc - len) >= 1024) +		new_alloc >>= 1; + +	for (size_t i = len; i < self->len; i++) +		free (self->items[i]); +	if (new_alloc != self->alloc) +		self->items = xreallocarray (self->items, +			sizeof *self->items, (self->alloc = new_alloc)); +	for (size_t i = self->len; i < len; i++) +		self->items[i] = NULL; + +	self->len = len; +} +  // --- Application -------------------------------------------------------------  // Function names are prefixed mostly because of curses which clutters the @@ -460,13 +573,6 @@ struct tab  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -struct playlist -{ -	struct str_map *items;              ///< Current playlist -	size_t len;                         ///< Length -	size_t alloc;                       ///< Allocated items -}; -  struct attrs  {  	short fg;                           ///< Foreground colour index @@ -508,7 +614,7 @@ static struct app_context  	int song_duration;                  ///< Song duration in seconds  	int volume;                         ///< Current volume -	struct playlist playlist;           ///< Current playlist +	struct item_list playlist;          ///< Current playlist  	uint32_t playlist_version;          ///< Playlist version  	// Data: @@ -566,68 +672,6 @@ tab_free (struct tab *self)  	free (self->name);  } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -static void -playlist_init (struct playlist *self) -{ -	memset (self, 0, sizeof *self); -	self->items = xcalloc (sizeof *self->items, (self->alloc = 16)); -} - -static void -playlist_free (struct playlist *self) -{ -	for (size_t i = 0; i < self->len; i++) -		str_map_free (&self->items[i]); -	free (self->items); -} - -static bool -playlist_set (struct playlist *self, int i, struct str_map *item) -{ -	if (i < 0 || (size_t) i >= self->len) -		return false; - -	str_map_free (&self->items[i]); -	self->items[i] = *item; -	return true; -} - -static struct str_map * -playlist_get (struct playlist *self, int i) -{ -	if (i < 0 || (size_t) i >= self->len) -		return false; - -	return &self->items[i]; -} - -static void -playlist_resize (struct playlist *self, size_t len) -{ -	// Make the allocated array big enough but not too large -	size_t new_alloc = self->alloc; -	while (new_alloc < len) -		new_alloc <<= 1; -	while (len < (new_alloc >> 2) -		&& new_alloc >= (STR_MAP_MIN_ALLOC << 1)) -		new_alloc >>= 1; - -	// Dispose of items that are out of range and resize the array if needed -	for (size_t i = len; i < self->len; i++) -		str_map_free (&self->items[i]); -	if (new_alloc != self->alloc) -		self->items = xreallocarray (self->items, -			sizeof *self->items, new_alloc); - -	// We need to initialize placeholders so that str_map_find() succeeds -	for (size_t i = self->len; i < len; i++) -		str_map_init (&self->items[i]); - -	self->len = len; -} -  // --- Configuration -----------------------------------------------------------  static struct config_schema g_config_settings[] = @@ -746,12 +790,8 @@ load_config_streams (struct config_item *subtree, void *user_data)  			print_warning ("`%s': stream URIs must be strings", iter.link->key);  		else  		{ -			struct str s; -			str_init (&s); -			str_append (&s, iter.link->key); -			str_append_c (&s, '\0'); -			str_append_str (&s, &item->value.string); -			str_vector_add_owned (&g_ctx.streams, str_steal (&s)); +			str_vector_add_owned (&g_ctx.streams, xstrdup_printf ("%s%c%s", +				iter.link->key, 0, item->value.string.str));  		}  	qsort (g_ctx.streams.vector, g_ctx.streams.len,  		sizeof *g_ctx.streams.vector, str_vector_sort_utf8_cb); @@ -809,7 +849,7 @@ app_init_context (void)  	mpd_client_init (&g_ctx.client, &g_ctx.poller);  	config_init (&g_ctx.config);  	str_vector_init (&g_ctx.streams); -	playlist_init (&g_ctx.playlist); +	item_list_init (&g_ctx.playlist);  	// This is also approximately what libunistring does internally,  	// since the locale name is canonicalized by locale_charset(). @@ -862,7 +902,7 @@ app_free_context (void)  	mpd_client_free (&g_ctx.client);  	str_map_free (&g_ctx.playback_info);  	str_vector_free (&g_ctx.streams); -	playlist_free (&g_ctx.playlist); +	item_list_free (&g_ctx.playlist);  	config_free (&g_ctx.config);  	poller_free (&g_ctx.poller); @@ -1112,9 +1152,8 @@ app_flush_header (struct row_buffer *buf, chtype attrs)  static void  app_draw_song_info (void)  { -	struct str_map *map; -	if (!(map = playlist_get (&g_ctx.playlist, g_ctx.song)) -	 || !soft_assert (map->len != 0)) +	compact_map_t map; +	if (!(map = item_list_get (&g_ctx.playlist, g_ctx.song)))  		return;  	// XXX: can we get rid of this and still make it look acceptable? @@ -1122,9 +1161,9 @@ app_draw_song_info (void)  	chtype a_highlight = APP_ATTR (HIGHLIGHT);  	char *title; -	if ((title = str_map_find (map, "title")) -	 || (title = str_map_find (map, "name")) -	 || (title = str_map_find (map, "file"))) +	if ((title = compact_map_find (map, "title")) +	 || (title = compact_map_find (map, "name")) +	 || (title = compact_map_find (map, "file")))  	{  		struct row_buffer buf;  		row_buffer_init (&buf); @@ -1132,8 +1171,8 @@ app_draw_song_info (void)  		app_flush_header (&buf, a_highlight);  	} -	char *artist = str_map_find (map, "artist"); -	char *album  = str_map_find (map, "album"); +	char *artist = compact_map_find (map, "artist"); +	char *album  = compact_map_find (map, "album");  	if (!artist && !album)  		return; @@ -1919,8 +1958,8 @@ current_tab_on_item_draw (size_t item_index, struct row_buffer *buffer,  	int width)  {  	// TODO: better output -	struct str_map *map = playlist_get (&g_ctx.playlist, item_index); -	row_buffer_append (buffer, str_map_find (map, "file"), +	compact_map_t map = item_list_get (&g_ctx.playlist, item_index); +	row_buffer_append (buffer, compact_map_find (map, "file"),  		(int) item_index == g_ctx.song ? A_BOLD : 0);  } @@ -2249,9 +2288,9 @@ info_tab_on_item_draw (size_t item_index, struct row_buffer *buffer, int width)  }  static void -info_tab_add (struct str_map *map, const char *field) +info_tab_add (compact_map_t data, const char *field)  { -	const char *value = str_map_find (map, field); +	const char *value = compact_map_find (data, field);  	if (!value) value = "";  	str_vector_add (&g_info_tab.keys, field); @@ -2266,8 +2305,8 @@ info_tab_update (void)  	str_vector_reset (&g_info_tab.values);  	g_info_tab.super.item_count = 0; -	struct str_map *map; -	if ((map = playlist_get (&g_ctx.playlist, g_ctx.song))) +	compact_map_t map; +	if ((map = item_list_get (&g_ctx.playlist, g_ctx.song)))  	{  		info_tab_add (map, "Title");  		info_tab_add (map, "Artist"); @@ -2471,13 +2510,15 @@ mpd_process_info_chunk (struct str_map *map, char *file)  	if (!file)  	{  		if (xstrtoul_map (map, "playlistlength", &n)) -			playlist_resize (&g_ctx.playlist, n); +			item_list_resize (&g_ctx.playlist, n);  		g_ctx.playback_info = *map;  	} -	else if (!xstrtoul_map (map, "pos", &n) -		|| !playlist_set (&g_ctx.playlist, n, map)) +	else +	{ +		if (xstrtoul_map (map, "pos", &n)) +			item_list_set (&g_ctx.playlist, n, map);  		str_map_free (map); - +	}  	mpd_init_response_map (map);  } | 
