forked from len0rd/rockbox
playlist: Optimize playlist_remove_all_tracks()
This was insanely slow for large playlists. Implement it as a constant-time operation to avoid the O(N!) blowup, and add a new "clear" command to the control file to avoid flooding it with individual delete commands. Change-Id: Ied82d1cba015591edd583f2a2ff6772108f4e176
This commit is contained in:
parent
2c4546ba99
commit
25bd3bc971
2 changed files with 89 additions and 21 deletions
109
apps/playlist.c
109
apps/playlist.c
|
@ -123,7 +123,16 @@
|
||||||
/* default load buffer size (should be at least 1 KiB) */
|
/* default load buffer size (should be at least 1 KiB) */
|
||||||
#define PLAYLIST_LOAD_BUFLEN (32*1024)
|
#define PLAYLIST_LOAD_BUFLEN (32*1024)
|
||||||
|
|
||||||
#define PLAYLIST_CONTROL_FILE_VERSION 2
|
/*
|
||||||
|
* Minimum supported version and current version of the control file.
|
||||||
|
* Any versions outside of this range will be rejected by the loader.
|
||||||
|
*
|
||||||
|
* v1 was the initial version when dynamic playlists were first implemented.
|
||||||
|
* v2 was added shortly thereafter and has been used since 2003.
|
||||||
|
* v3 added the (C)lear command and is otherwise identical to v2.
|
||||||
|
*/
|
||||||
|
#define PLAYLIST_CONTROL_FILE_MIN_VERSION 2
|
||||||
|
#define PLAYLIST_CONTROL_FILE_VERSION 3
|
||||||
|
|
||||||
#define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
|
#define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
|
||||||
|
|
||||||
|
@ -432,6 +441,9 @@ static int flush_cached_control_unlocked(struct playlist_info* playlist)
|
||||||
case PLAYLIST_COMMAND_RESET:
|
case PLAYLIST_COMMAND_RESET:
|
||||||
result = fdprintf(playlist->control_fd, "%s\n", "R");
|
result = fdprintf(playlist->control_fd, "%s\n", "R");
|
||||||
break;
|
break;
|
||||||
|
case PLAYLIST_COMMAND_CLEAR:
|
||||||
|
result = fdprintf(playlist->control_fd, "%s\n", "C");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1343,6 +1355,53 @@ static int create_and_play_dir(int direction, bool play_last)
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* remove all tracks, leaving the current track queued
|
||||||
|
*/
|
||||||
|
static int remove_all_tracks_unlocked(struct playlist_info *playlist, bool write)
|
||||||
|
{
|
||||||
|
if (playlist->amount <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Move current track down to position 0 */
|
||||||
|
playlist->indices[0] = playlist->indices[playlist->index];
|
||||||
|
#ifdef HAVE_DIRCACHE
|
||||||
|
if (playlist->dcfrefs_handle)
|
||||||
|
{
|
||||||
|
struct dircache_fileref *dcfrefs =
|
||||||
|
core_get_data(playlist->dcfrefs_handle);
|
||||||
|
dcfrefs[0] = dcfrefs[playlist->index];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Update playlist state as if by remove_track_unlocked() */
|
||||||
|
bool inserted = playlist->indices[0] & PLAYLIST_INSERT_TYPE_MASK;
|
||||||
|
|
||||||
|
playlist->index = 0;
|
||||||
|
playlist->amount = 1;
|
||||||
|
playlist->indices[0] |= PLAYLIST_QUEUED;
|
||||||
|
|
||||||
|
if (inserted)
|
||||||
|
playlist->num_inserted_tracks = 1;
|
||||||
|
else
|
||||||
|
playlist->deleted = true;
|
||||||
|
|
||||||
|
playlist->first_index = 0;
|
||||||
|
|
||||||
|
if (playlist->last_insert_pos == 0)
|
||||||
|
playlist->last_insert_pos = -1;
|
||||||
|
else
|
||||||
|
playlist->last_insert_pos = 0;
|
||||||
|
|
||||||
|
if (write && playlist->control_fd >= 0)
|
||||||
|
{
|
||||||
|
update_control(playlist, PLAYLIST_COMMAND_CLEAR, -1, -1, NULL, NULL, NULL);
|
||||||
|
sync_control(playlist, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add track to playlist at specified position. There are seven special
|
* Add track to playlist at specified position. There are seven special
|
||||||
* positions that can be specified:
|
* positions that can be specified:
|
||||||
|
@ -1444,7 +1503,7 @@ static int add_track_to_playlist_unlocked(struct playlist_info* playlist,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PLAYLIST_REPLACE:
|
case PLAYLIST_REPLACE:
|
||||||
if (playlist_remove_all_tracks(playlist) < 0)
|
if (remove_all_tracks_unlocked(playlist, true) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
int newpos = playlist->index + 1;
|
int newpos = playlist->index + 1;
|
||||||
playlist->last_insert_pos = position = insert_position = newpos;
|
playlist->last_insert_pos = position = insert_position = newpos;
|
||||||
|
@ -2635,7 +2694,7 @@ int playlist_insert_directory(struct playlist_info* playlist,
|
||||||
|
|
||||||
if (position == PLAYLIST_REPLACE)
|
if (position == PLAYLIST_REPLACE)
|
||||||
{
|
{
|
||||||
if (playlist_remove_all_tracks(playlist) == 0)
|
if (remove_all_tracks_unlocked(playlist, true) == 0)
|
||||||
position = PLAYLIST_INSERT_LAST;
|
position = PLAYLIST_INSERT_LAST;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -2727,7 +2786,7 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam
|
||||||
|
|
||||||
if (position == PLAYLIST_REPLACE)
|
if (position == PLAYLIST_REPLACE)
|
||||||
{
|
{
|
||||||
if (playlist_remove_all_tracks(playlist) == 0)
|
if (remove_all_tracks_unlocked(playlist, true) == 0)
|
||||||
position = PLAYLIST_INSERT_LAST;
|
position = PLAYLIST_INSERT_LAST;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -3221,8 +3280,6 @@ int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
|
||||||
/*
|
/*
|
||||||
* Removes all tracks, from the playlist, leaving the presently playing
|
* Removes all tracks, from the playlist, leaving the presently playing
|
||||||
* track queued.
|
* track queued.
|
||||||
*
|
|
||||||
* TODO: This is insanely slow for huge playlists. Must fix.
|
|
||||||
*/
|
*/
|
||||||
int playlist_remove_all_tracks(struct playlist_info *playlist)
|
int playlist_remove_all_tracks(struct playlist_info *playlist)
|
||||||
{
|
{
|
||||||
|
@ -3234,23 +3291,11 @@ int playlist_remove_all_tracks(struct playlist_info *playlist)
|
||||||
dc_thread_stop(playlist);
|
dc_thread_stop(playlist);
|
||||||
playlist_mutex_lock(&playlist->mutex);
|
playlist_mutex_lock(&playlist->mutex);
|
||||||
|
|
||||||
while (playlist->index > 0)
|
result = remove_all_tracks_unlocked(playlist, true);
|
||||||
if ((result = remove_track_unlocked(playlist, 0, true)) < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
while (playlist->amount > 1)
|
|
||||||
if ((result = remove_track_unlocked(playlist, 1, true)) < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (playlist->amount == 1) {
|
|
||||||
playlist->indices[0] |= PLAYLIST_QUEUED;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
playlist_mutex_unlock(&playlist->mutex);
|
playlist_mutex_unlock(&playlist->mutex);
|
||||||
dc_thread_start(playlist, false);
|
dc_thread_start(playlist, false);
|
||||||
|
return result;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -3384,7 +3429,17 @@ int playlist_resume(void)
|
||||||
|
|
||||||
version = atoi(str1);
|
version = atoi(str1);
|
||||||
|
|
||||||
if (version != PLAYLIST_CONTROL_FILE_VERSION)
|
/*
|
||||||
|
* TODO: Playlist control file version upgrades
|
||||||
|
*
|
||||||
|
* If an older version control file is loaded then
|
||||||
|
* the header should be updated to the latest version
|
||||||
|
* in case any incompatible commands are written out.
|
||||||
|
* (It's not a big deal since the error message will
|
||||||
|
* be practically the same either way...)
|
||||||
|
*/
|
||||||
|
if (version < PLAYLIST_CONTROL_FILE_MIN_VERSION ||
|
||||||
|
version > PLAYLIST_CONTROL_FILE_VERSION)
|
||||||
{
|
{
|
||||||
result = -3;
|
result = -3;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -3523,6 +3578,15 @@ int playlist_resume(void)
|
||||||
playlist->last_insert_pos = -1;
|
playlist->last_insert_pos = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case PLAYLIST_COMMAND_CLEAR:
|
||||||
|
{
|
||||||
|
if (remove_all_tracks_unlocked(playlist, false) < 0)
|
||||||
|
{
|
||||||
|
result = -16;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case PLAYLIST_COMMAND_COMMENT:
|
case PLAYLIST_COMMAND_COMMENT:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -3570,6 +3634,9 @@ int playlist_resume(void)
|
||||||
case 'R':
|
case 'R':
|
||||||
current_command = PLAYLIST_COMMAND_RESET;
|
current_command = PLAYLIST_COMMAND_RESET;
|
||||||
break;
|
break;
|
||||||
|
case 'C':
|
||||||
|
current_command = PLAYLIST_COMMAND_CLEAR;
|
||||||
|
break;
|
||||||
case '#':
|
case '#':
|
||||||
current_command = PLAYLIST_COMMAND_COMMENT;
|
current_command = PLAYLIST_COMMAND_COMMENT;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -47,6 +47,7 @@ enum playlist_command {
|
||||||
PLAYLIST_COMMAND_SHUFFLE,
|
PLAYLIST_COMMAND_SHUFFLE,
|
||||||
PLAYLIST_COMMAND_UNSHUFFLE,
|
PLAYLIST_COMMAND_UNSHUFFLE,
|
||||||
PLAYLIST_COMMAND_RESET,
|
PLAYLIST_COMMAND_RESET,
|
||||||
|
PLAYLIST_COMMAND_CLEAR,
|
||||||
PLAYLIST_COMMAND_COMMENT
|
PLAYLIST_COMMAND_COMMENT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue