Merge pull request #11158 from schellingb/playlist_rbuf

Change playlists to use a dynamic array
This commit is contained in:
Autechre 2020-08-07 17:41:47 +02:00 committed by GitHub
commit e64d5d2310
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 270 additions and 163 deletions

View file

@ -4841,7 +4841,7 @@ MSG_HASH(
)
MSG_HASH(
MENU_ENUM_SUBLABEL_CONTENT_FAVORITES_SIZE,
"Limit the number of entries in the favorites playlist. Once limit is reached, new additions will be prevented until old entries are removed. Setting a value of -1 allows 'unlimited' (99999) entries. WARNING: Reducing the value will delete existing entries!"
"Limit the number of entries in the favorites playlist. Once limit is reached, new additions will be prevented until old entries are removed. Setting a value of -1 allows 'unlimited' entries. WARNING: Reducing the value will delete existing entries!"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_PLAYLIST_ENTRY_RENAME,

View file

@ -0,0 +1,107 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (rbuf.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_ARRAY_RBUF_H__
#define __LIBRETRO_SDK_ARRAY_RBUF_H__
/*
* This file implements stretchy buffers as invented (?) by Sean Barrett.
* Based on the implementation from the public domain Bitwise project
* by Per Vognsen - https://github.com/pervognsen/bitwise
*
* It's a super simple type safe dynamic array for C with no need
* to predeclare any type or anything.
* The first time an element is added, memory for 16 elements are allocated.
* Then every time length is about to exceed capacity, capacity is doubled.
*
* Sample usage:
*
* mytype_t* buf = NULL;
* RBUF_PUSH(buf, some_element);
* RBUF_PUSH(buf, other_element);
* -- now RBUF_LEN(buf) == 2, buf[0] == some_element, buf[1] == other_element
*
* -- Free allocated memory:
* RBUF_FREE(buf);
* -- now buf == NULL, RBUF_LEN(buf) == 0, RBUF_CAP(buf) == 0
*
* -- Explicitly increase allocated memory and set capacity:
* RBUF_FIT(buf, 100);
* -- now RBUF_LEN(buf) == 0, RBUF_CAP(buf) == 100
*
* -- Resize buffer (does not initialize or zero memory!)
* RBUF_RESIZE(buf, 200);
* -- now RBUF_LEN(buf) == 200, RBUF_CAP(buf) == 200
*
* -- To handle running out of memory:
* bool ran_out_of_memory = !RBUF_TRYFIT(buf, 1000);
* -- before RESIZE or PUSH. When out of memory, buf will stay unmodified.
*/
#include <retro_math.h> /* for MAX */
#include <stdlib.h> /* for malloc, realloc */
#define RBUF__HDR(b) (((struct rbuf__hdr *)(b))-1)
#define RBUF_LEN(b) ((b) ? RBUF__HDR(b)->len : 0)
#define RBUF_CAP(b) ((b) ? RBUF__HDR(b)->cap : 0)
#define RBUF_END(b) ((b) + RBUF_LEN(b))
#define RBUF_SIZEOF(b) ((b) ? RBUF_LEN(b)*sizeof(*b) : 0)
#define RBUF_FREE(b) ((b) ? (free(RBUF__HDR(b)), (b) = NULL) : 0)
#define RBUF_FIT(b, n) ((size_t)(n) <= RBUF_CAP(b) ? 0 : (*(void**)(&(b)) = rbuf__grow((b), (n), sizeof(*(b)))))
#define RBUF_PUSH(b, val) (RBUF_FIT((b), 1 + RBUF_LEN(b)), (b)[RBUF__HDR(b)->len++] = (val))
#define RBUF_POP(b) (b)[--RBUF__HDR(b)->len]
#define RBUF_RESIZE(b, sz) (RBUF_FIT((b), (sz)), ((b) ? RBUF__HDR(b)->len = (sz) : 0))
#define RBUF_CLEAR(b) ((b) ? RBUF__HDR(b)->len = 0 : 0)
#define RBUF_TRYFIT(b, n) (RBUF_FIT((b), (n)), (((b) && RBUF_CAP(b) >= (size_t)(n)) || !(n)))
struct rbuf__hdr
{
size_t len;
size_t cap;
};
static void *rbuf__grow(void *buf,
size_t new_len, size_t elem_size)
{
struct rbuf__hdr *new_hdr;
size_t new_cap = MAX(2 * RBUF_CAP(buf), MAX(new_len, 16));
size_t new_size = sizeof(struct rbuf__hdr) + new_cap*elem_size;
if (buf)
{
new_hdr = (struct rbuf__hdr *)realloc(RBUF__HDR(buf), new_size);
if (!new_hdr)
return buf; /* out of memory, return unchanged */
}
else
{
new_hdr = (struct rbuf__hdr *)malloc(new_size);
if (!new_hdr)
return NULL; /* out of memory */
new_hdr->len = 0;
}
new_hdr->cap = new_cap;
return new_hdr + 1;
}
#endif

View file

@ -23,21 +23,7 @@
#include "../libretro-db/libretrodb.h"
#include <compat/strcasestr.h>
#include <compat/strl.h>
/* Stretchy buffers, invented (?) by Sean Barrett */
#define EX_BUF__HDR(b) (((struct ex_buf_hdr *)(b))-1)
#define EX_BUF_LEN(b) ((b) ? EX_BUF__HDR(b)->len : 0)
#define EX_BUF_CAP(b) ((b) ? EX_BUF__HDR(b)->cap : 0)
#define EX_BUF_END(b) ((b) + EX_BUF_LEN(b))
#define EX_BUF_SIZEOF(b) ((b) ? EX_BUF_LEN(b)*sizeof(*b) : 0)
#define EX_BUF_FREE(b) ((b) ? (free(EX_BUF__HDR(b)), (b) = NULL) : 0)
#define EX_BUF_FIT(b, n) ((size_t)(n) <= EX_BUF_CAP(b) ? 0 : (*(void**)(&(b)) = ex_buf__grow((b), (n), sizeof(*(b)))))
#define EX_BUF_PUSH(b, val) (EX_BUF_FIT((b), 1 + EX_BUF_LEN(b)), (b)[EX_BUF__HDR(b)->len++] = (val))
#define EX_BUF_POP(b) (b)[--EX_BUF__HDR(b)->len]
#define EX_BUF_RESIZE(b, sz) (EX_BUF_FIT((b), (sz)), ((b) ? EX_BUF__HDR(b)->len = (sz) : 0))
#define EX_BUF_CLEAR(b) ((b) ? EX_BUF__HDR(b)->len = 0 : 0)
#include <array/rbuf.h>
#define EX_ARENA_ALIGNMENT 8
#define EX_ARENA_BLOCK_SIZE (64 * 1024)
@ -70,12 +56,6 @@ enum
EXPLORE_TYPE_FIRSTITEM = EXPLORE_TYPE_FIRSTCATEGORY + EXPLORE_CAT_COUNT
};
struct ex_buf_hdr
{
size_t len;
size_t cap;
};
/* Arena allocator */
typedef struct ex_arena
{
@ -147,30 +127,13 @@ explore_by_info[EXPLORE_CAT_COUNT] =
/* TODO/FIXME - static global */
static explore_state_t* explore_state;
static void *ex_buf__grow(const void *buf,
size_t new_len, size_t elem_size)
{
struct ex_buf_hdr *new_hdr;
size_t new_cap = MAX(2 * EX_BUF_CAP(buf), MAX(new_len, 16));
size_t new_size = sizeof(struct ex_buf_hdr) + new_cap*elem_size;
if (buf)
new_hdr = (struct ex_buf_hdr *)realloc(EX_BUF__HDR(buf), new_size);
else
{
new_hdr = (struct ex_buf_hdr *)malloc(new_size);
new_hdr->len = 0;
}
new_hdr->cap = new_cap;
return new_hdr + 1;
}
static void ex_arena_grow(ex_arena *arena, size_t min_size)
{
size_t size = EX_ARENA_ALIGN_UP(
MAX(min_size, EX_ARENA_BLOCK_SIZE), EX_ARENA_ALIGNMENT);
arena->ptr = (char *)malloc(size);
arena->end = arena->ptr + size;
EX_BUF_PUSH(arena->blocks, arena->ptr);
RBUF_PUSH(arena->blocks, arena->ptr);
}
static void *ex_arena_alloc(ex_arena *arena, size_t size)
@ -190,10 +153,10 @@ static void ex_arena_free(ex_arena *arena)
{
char **it;
for (it = arena->blocks; it != EX_BUF_END(arena->blocks); it++)
for (it = arena->blocks; it != RBUF_END(arena->blocks); it++)
free(*it);
EX_BUF_FREE(arena->blocks);
RBUF_FREE(arena->blocks);
arena->ptr = NULL;
arena->end = NULL;
arena->blocks = NULL;
@ -459,14 +422,14 @@ static void explore_add_unique_string(
sizeof(explore_string_t) + len);
memcpy(entry->str, str, len);
entry->str[len] = '\0';
EX_BUF_PUSH(explore->by[cat], entry);
RBUF_PUSH(explore->by[cat], entry);
ex_hashmap32_setptr(&maps[cat], hash, entry);
}
if (!e->by[cat])
e->by[cat] = entry;
else
EX_BUF_PUSH(*split_buf, entry);
RBUF_PUSH(*split_buf, entry);
if (*p_next == '\0')
return;
@ -493,7 +456,7 @@ static void explore_unload_icons(explore_state_t *state)
unsigned i;
if (!state)
return;
for (i = 0; i != EX_BUF_LEN(state->icons); i++)
for (i = 0; i != RBUF_LEN(state->icons); i++)
if (state->icons[i])
video_driver_texture_unload(&state->icons[i]);
}
@ -504,16 +467,16 @@ static void explore_free(explore_state_t *state)
if (!state)
return;
for (i = 0; i != EXPLORE_CAT_COUNT; i++)
EX_BUF_FREE(state->by[i]);
RBUF_FREE(state->by[i]);
EX_BUF_FREE(state->entries);
RBUF_FREE(state->entries);
for (i = 0; i != EX_BUF_LEN(state->playlists); i++)
for (i = 0; i != RBUF_LEN(state->playlists); i++)
playlist_free(state->playlists[i]);
EX_BUF_FREE(state->playlists);
RBUF_FREE(state->playlists);
explore_unload_icons(state);
EX_BUF_FREE(state->icons);
RBUF_FREE(state->icons);
ex_arena_free(&state->arena);
}
@ -525,8 +488,14 @@ static void explore_load_icons(explore_state_t *state)
if (!state)
return;
system_count = EX_BUF_LEN(state->by[EXPLORE_BY_SYSTEM]);
EX_BUF_RESIZE(state->icons, system_count);
system_count = RBUF_LEN(state->by[EXPLORE_BY_SYSTEM]);
/* unload any icons that could exist from a previous call to this */
explore_unload_icons(state);
/* RBUF_RESIZE leaves memory uninitialised, have to zero it 'manually' */
RBUF_RESIZE(state->icons, system_count);
memset(state->icons, 0, RBUF_SIZEOF(state->icons));
fill_pathname_application_special(path, sizeof(path),
APPLICATION_SPECIAL_DIRECTORY_ASSETS_SYSICONS);
@ -539,7 +508,6 @@ static void explore_load_icons(explore_state_t *state)
for (i = 0; i != system_count; i++)
{
struct texture_image ti;
state->icons[i] = 0;
strlcpy(path + pathlen,
state->by[EXPLORE_BY_SYSTEM][i]->str, sizeof(path) - pathlen);
@ -689,8 +657,8 @@ static explore_state_t *explore_build_list(void)
continue;
}
EX_BUF_PUSH(rdbs, newrdb);
rdb_num = (uintptr_t)EX_BUF_LEN(rdbs);
RBUF_PUSH(rdbs, newrdb);
rdb_num = (uintptr_t)RBUF_LEN(rdbs);
ex_hashmap32_setnum(&rdb_indices, rdb_hash, rdb_num);
}
@ -715,14 +683,14 @@ static explore_state_t *explore_build_list(void)
}
if (used_entries)
EX_BUF_PUSH(explore->playlists, playlist);
RBUF_PUSH(explore->playlists, playlist);
else
playlist_free(playlist);
}
/* Loop through all RDBs referenced in the playlists
* and load meta data strings */
for (i = 0; i != EX_BUF_LEN(rdbs); i++)
for (i = 0; i != RBUF_LEN(rdbs); i++)
{
struct rmsgpack_dom_value item;
struct explore_rdb* rdb = &rdbs[i];
@ -841,19 +809,19 @@ static explore_state_t *explore_build_list(void)
}
#endif
if (EX_BUF_LEN(split_buf))
if (RBUF_LEN(split_buf))
{
size_t len;
EX_BUF_PUSH(split_buf, NULL); /* terminator */
len = EX_BUF_SIZEOF(split_buf);
RBUF_PUSH(split_buf, NULL); /* terminator */
len = RBUF_SIZEOF(split_buf);
e.split = (explore_string_t **)
ex_arena_alloc(&explore->arena, len);
memcpy(e.split, split_buf, len);
EX_BUF_CLEAR(split_buf);
RBUF_CLEAR(split_buf);
}
EX_BUF_PUSH(explore->entries, e);
RBUF_PUSH(explore->entries, e);
/* if all entries have found connections, we can leave early */
if (--rdb->count == 0)
@ -870,14 +838,14 @@ static explore_state_t *explore_build_list(void)
ex_hashmap32_free(&rdb->playlist_crcs);
ex_hashmap32_free(&rdb->playlist_names);
}
EX_BUF_FREE(split_buf);
RBUF_FREE(split_buf);
ex_hashmap32_free(&rdb_indices);
EX_BUF_FREE(rdbs);
RBUF_FREE(rdbs);
for (i = 0; i != EXPLORE_CAT_COUNT; i++)
{
uint32_t idx;
size_t len = EX_BUF_LEN(explore->by[i]);
size_t len = RBUF_LEN(explore->by[i]);
if (explore->by[i])
qsort(explore->by[i], len, sizeof(*explore->by[i]),
@ -889,7 +857,7 @@ static explore_state_t *explore_build_list(void)
ex_hashmap32_free(&cat_maps[i]);
}
qsort(explore->entries,
EX_BUF_LEN(explore->entries),
RBUF_LEN(explore->entries),
sizeof(*explore->entries), explore_qsort_func_entries);
return explore;
}
@ -1085,7 +1053,7 @@ unsigned menu_displaylist_explore(file_list_t *list)
explore_string_t **entries = explore_state->by[cat];
size_t tmplen;
if (!EX_BUF_LEN(entries))
if (!RBUF_LEN(entries))
continue;
for (i = 1; i < depth; i++)
@ -1100,14 +1068,14 @@ unsigned menu_displaylist_explore(file_list_t *list)
if (explore_by_info[cat].is_numeric)
{
snprintf(tmp + tmplen, sizeof(tmp) - tmplen, " (%s - %s)",
entries[0]->str, entries[EX_BUF_LEN(entries) - 1]->str);
entries[0]->str, entries[RBUF_LEN(entries) - 1]->str);
}
else
{
strlcat(tmp, " (", sizeof(tmp));
snprintf(tmp + tmplen + 2, sizeof(tmp) - tmplen - 2,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_ITEMS_COUNT),
(unsigned)EX_BUF_LEN(entries));
(unsigned)RBUF_LEN(entries));
strlcat(tmp, ")", sizeof(tmp));
}
}
@ -1133,7 +1101,7 @@ SKIP_EXPLORE_BY_CATEGORY:;
{
/* List all items in a selected explore by category */
explore_string_t **entries = explore_state->by[current_cat];
unsigned i_last = EX_BUF_LEN(entries) - 1;
unsigned i_last = RBUF_LEN(entries) - 1;
for (i = 0; i <= i_last; i++)
explore_menu_entry(list, explore_state,
entries[i]->str, EXPLORE_TYPE_FIRSTITEM + i);
@ -1223,7 +1191,7 @@ SKIP_EXPLORE_BY_CATEGORY:;
}
e = explore_state->entries;
e_end = EX_BUF_END(explore_state->entries);
e_end = RBUF_END(explore_state->entries);
for (; e != e_end; e++)
{
@ -1307,7 +1275,7 @@ SKIP_ENTRY:;
strlcpy(explore_state->title,
pl_entry->label, sizeof(explore_state->title));
for (pl_idx = 0; pl_idx != EX_BUF_LEN(explore_state->playlists); pl_idx++)
for (pl_idx = 0; pl_idx != RBUF_LEN(explore_state->playlists); pl_idx++)
{
menu_displaylist_info_t info;
const struct playlist_entry* pl_first = NULL;
@ -1348,12 +1316,12 @@ uintptr_t menu_explore_get_entry_icon(unsigned type)
if (explore_state->show_icons == EXPLORE_ICONS_CONTENT)
{
explore_entry_t* e = &explore_state->entries[i];
if (e < EX_BUF_END(explore_state->entries))
if (e < RBUF_END(explore_state->entries))
return explore_state->icons[e->by[EXPLORE_BY_SYSTEM]->idx];
}
else if (explore_state->show_icons == EXPLORE_ICONS_SYSTEM_CATEGORY)
{
if (i < EX_BUF_LEN(explore_state->icons))
if (i < RBUF_LEN(explore_state->icons))
return explore_state->icons[i];
}
return 0;

View file

@ -30,6 +30,7 @@
#include <file/file_path.h>
#include <lists/string_list.h>
#include <formats/jsonsax_full.h>
#include <array/rbuf.h>
#include "playlist.h"
#include "verbosity.h"
@ -63,8 +64,6 @@ struct content_playlist
enum playlist_thumbnail_mode left_thumbnail_mode;
enum playlist_sort_mode sort_mode;
size_t size;
char *default_core_path;
char *default_core_name;
char *base_content_directory;
@ -78,6 +77,7 @@ typedef struct
bool in_items;
bool in_subsystem_roms;
bool capacity_exceeded;
bool out_of_memory;
unsigned array_depth;
unsigned object_depth;
@ -355,7 +355,7 @@ uint32_t playlist_get_size(playlist_t *playlist)
{
if (!playlist)
return 0;
return (uint32_t)playlist->size;
return (uint32_t)RBUF_LEN(playlist->entries);
}
char *playlist_get_conf_path(playlist_t *playlist)
@ -379,7 +379,7 @@ void playlist_get_index(playlist_t *playlist,
size_t idx,
const struct playlist_entry **entry)
{
if (!playlist || !entry || (idx >= playlist->size))
if (!playlist || !entry || (idx >= RBUF_LEN(playlist->entries)))
return;
*entry = &playlist->entries[idx];
@ -452,16 +452,16 @@ static void playlist_free_entry(struct playlist_entry *entry)
void playlist_delete_index(playlist_t *playlist,
size_t idx)
{
struct playlist_entry *entry_to_delete = NULL;
size_t len;
struct playlist_entry *entry_to_delete;
if (!playlist)
return;
if (idx >= playlist->size)
len = RBUF_LEN(playlist->entries);
if (idx >= len)
return;
playlist->size = playlist->size - 1;
/* Free unwanted entry */
entry_to_delete = (struct playlist_entry *)(playlist->entries + idx);
if (entry_to_delete)
@ -469,7 +469,9 @@ void playlist_delete_index(playlist_t *playlist,
/* Shift remaining entries to fill the gap */
memmove(playlist->entries + idx, playlist->entries + idx + 1,
(playlist->size - idx) * sizeof(struct playlist_entry));
(len - 1 - idx) * sizeof(struct playlist_entry));
RBUF_RESIZE(playlist->entries, len - 1);
playlist->modified = true;
}
@ -497,7 +499,7 @@ void playlist_delete_by_path(playlist_t *playlist,
strlcpy(real_search_path, search_path, sizeof(real_search_path));
path_resolve_realpath(real_search_path, sizeof(real_search_path), true);
while (i < playlist->size)
while (i < RBUF_LEN(playlist->entries))
{
if (!playlist_path_equal(real_search_path, playlist->entries[i].path,
&playlist->config))
@ -518,7 +520,7 @@ void playlist_get_index_by_path(playlist_t *playlist,
const char *search_path,
const struct playlist_entry **entry)
{
size_t i;
size_t i, len;
char real_search_path[PATH_MAX_LENGTH];
real_search_path[0] = '\0';
@ -530,7 +532,7 @@ void playlist_get_index_by_path(playlist_t *playlist,
strlcpy(real_search_path, search_path, sizeof(real_search_path));
path_resolve_realpath(real_search_path, sizeof(real_search_path), true);
for (i = 0; i < playlist->size; i++)
for (i = 0, len = RBUF_LEN(playlist->entries); i < len; i++)
{
if (!playlist_path_equal(real_search_path, playlist->entries[i].path,
&playlist->config))
@ -545,7 +547,7 @@ void playlist_get_index_by_path(playlist_t *playlist,
bool playlist_entry_exists(playlist_t *playlist,
const char *path)
{
size_t i;
size_t i, len;
char real_search_path[PATH_MAX_LENGTH];
real_search_path[0] = '\0';
@ -557,7 +559,7 @@ bool playlist_entry_exists(playlist_t *playlist,
strlcpy(real_search_path, path, sizeof(real_search_path));
path_resolve_realpath(real_search_path, sizeof(real_search_path), true);
for (i = 0; i < playlist->size; i++)
for (i = 0, len = RBUF_LEN(playlist->entries); i < len; i++)
if (playlist_path_equal(real_search_path, playlist->entries[i].path,
&playlist->config))
return true;
@ -570,7 +572,7 @@ void playlist_update(playlist_t *playlist, size_t idx,
{
struct playlist_entry *entry = NULL;
if (!playlist || idx > playlist->size)
if (!playlist || idx >= RBUF_LEN(playlist->entries))
return;
entry = &playlist->entries[idx];
@ -631,7 +633,7 @@ void playlist_update_runtime(playlist_t *playlist, size_t idx,
{
struct playlist_entry *entry = NULL;
if (!playlist || idx > playlist->size)
if (!playlist || idx >= RBUF_LEN(playlist->entries))
return;
entry = &playlist->entries[idx];
@ -736,7 +738,7 @@ void playlist_update_runtime(playlist_t *playlist, size_t idx,
bool playlist_push_runtime(playlist_t *playlist,
const struct playlist_entry *entry)
{
size_t i;
size_t i, len;
char real_path[PATH_MAX_LENGTH];
char real_core_path[PATH_MAX_LENGTH];
@ -771,7 +773,8 @@ bool playlist_push_runtime(playlist_t *playlist,
return false;
}
for (i = 0; i < playlist->size; i++)
len = RBUF_LEN(playlist->entries);
for (i = 0; i < len; i++)
{
struct playlist_entry tmp;
const char *entry_path = playlist->entries[i].path;
@ -801,19 +804,27 @@ bool playlist_push_runtime(playlist_t *playlist,
goto success;
}
if (playlist->size == playlist->config.capacity)
{
struct playlist_entry *last_entry = &playlist->entries[playlist->config.capacity - 1];
if (playlist->config.capacity == 0)
return false;
if (last_entry)
playlist_free_entry(last_entry);
playlist->size--;
if (len == playlist->config.capacity)
{
struct playlist_entry *last_entry = &playlist->entries[len - 1];
playlist_free_entry(last_entry);
len--;
}
else
{
/* Allocate memory to fit one more item and resize the buffer */
if (!RBUF_TRYFIT(playlist->entries, len + 1))
return false; /* out of memory */
RBUF_RESIZE(playlist->entries, len + 1);
}
if (playlist->entries)
{
memmove(playlist->entries + 1, playlist->entries,
(playlist->config.capacity - 1) * sizeof(struct playlist_entry));
len * sizeof(struct playlist_entry));
playlist->entries[0].path = NULL;
playlist->entries[0].core_path = NULL;
@ -843,8 +854,6 @@ bool playlist_push_runtime(playlist_t *playlist,
playlist->entries[0].last_played_str = strdup(entry->last_played_str);
}
playlist->size++;
success:
playlist->modified = true;
@ -904,7 +913,7 @@ void playlist_resolve_path(enum playlist_file_mode mode,
bool playlist_push(playlist_t *playlist,
const struct playlist_entry *entry)
{
size_t i;
size_t i, len;
char real_path[PATH_MAX_LENGTH];
char real_core_path[PATH_MAX_LENGTH];
const char *core_name = entry->core_name;
@ -954,7 +963,8 @@ bool playlist_push(playlist_t *playlist,
}
}
for (i = 0; i < playlist->size; i++)
len = RBUF_LEN(playlist->entries);
for (i = 0; i < len; i++)
{
struct playlist_entry tmp;
const char *entry_path = playlist->entries[i].path;
@ -1068,20 +1078,27 @@ bool playlist_push(playlist_t *playlist,
goto success;
}
if (playlist->size == playlist->config.capacity)
{
struct playlist_entry *last_entry =
&playlist->entries[playlist->config.capacity - 1];
if (playlist->config.capacity == 0)
return false;
if (last_entry)
playlist_free_entry(last_entry);
playlist->size--;
if (len == playlist->config.capacity)
{
struct playlist_entry *last_entry = &playlist->entries[len - 1];
playlist_free_entry(last_entry);
len--;
}
else
{
/* Allocate memory to fit one more item and resize the buffer */
if (!RBUF_TRYFIT(playlist->entries, len + 1))
return false; /* out of memory */
RBUF_RESIZE(playlist->entries, len + 1);
}
if (playlist->entries)
{
memmove(playlist->entries + 1, playlist->entries,
(playlist->config.capacity - 1) * sizeof(struct playlist_entry));
len * sizeof(struct playlist_entry));
playlist->entries[0].path = NULL;
playlist->entries[0].label = NULL;
@ -1132,8 +1149,6 @@ bool playlist_push(playlist_t *playlist,
}
}
playlist->size++;
success:
playlist->modified = true;
@ -1170,7 +1185,7 @@ static void JSONLogError(JSONContext *pCtx)
void playlist_write_runtime_file(playlist_t *playlist)
{
size_t i;
size_t i, len;
intfstream_t *file = NULL;
JSONContext context = {0};
@ -1218,7 +1233,7 @@ void playlist_write_runtime_file(playlist_t *playlist)
JSON_Writer_WriteStartArray(context.writer);
JSON_Writer_WriteNewLine(context.writer);
for (i = 0; i < playlist->size; i++)
for (i = 0, len = RBUF_LEN(playlist->entries); i < len; i++)
{
JSON_Writer_WriteSpace(context.writer, 4);
JSON_Writer_WriteStartObject(context.writer);
@ -1379,7 +1394,7 @@ void playlist_write_runtime_file(playlist_t *playlist)
JSON_Writer_WriteSpace(context.writer, 4);
JSON_Writer_WriteEndObject(context.writer);
if (i < playlist->size - 1)
if (i < len - 1)
JSON_Writer_WriteComma(context.writer);
JSON_Writer_WriteNewLine(context.writer);
@ -1416,7 +1431,7 @@ static JSON_Status JSON_CALL JSON_Writer_WriteSpace_NULL(JSON_Writer writer, siz
void playlist_write_file(playlist_t *playlist)
{
size_t i;
size_t i, len;
intfstream_t *file = NULL;
bool compressed = false;
@ -1457,7 +1472,7 @@ void playlist_write_file(playlist_t *playlist)
#ifdef RARCH_INTERNAL
if (playlist->config.old_format)
{
for (i = 0; i < playlist->size; i++)
for (i = 0, len = RBUF_LEN(playlist->entries); i < len; i++)
intfstream_printf(file, "%s\n%s\n%s\n%s\n%s\n%s\n",
playlist->entries[i].path ? playlist->entries[i].path : "",
playlist->entries[i].label ? playlist->entries[i].label : "",
@ -1640,7 +1655,7 @@ void playlist_write_file(playlist_t *playlist)
JSON_Writer_WriteStartArray(context.writer);
json_write_new_line(context.writer);
for (i = 0; i < playlist->size; i++)
for (i = 0, len = RBUF_LEN(playlist->entries); i < len; i++)
{
json_write_space(context.writer, 4);
JSON_Writer_WriteStartObject(context.writer);
@ -1813,7 +1828,7 @@ void playlist_write_file(playlist_t *playlist)
json_write_space(context.writer, 4);
JSON_Writer_WriteEndObject(context.writer);
if (i < playlist->size - 1)
if (i < len - 1)
JSON_Writer_WriteComma(context.writer);
json_write_new_line(context.writer);
@ -1846,7 +1861,7 @@ end:
*/
void playlist_free(playlist_t *playlist)
{
size_t i;
size_t i, len;
if (!playlist)
return;
@ -1865,7 +1880,7 @@ void playlist_free(playlist_t *playlist)
if (playlist->entries)
{
for (i = 0; i < playlist->size; i++)
for (i = 0, len = RBUF_LEN(playlist->entries); i < len; i++)
{
struct playlist_entry *entry = &playlist->entries[i];
@ -1873,8 +1888,7 @@ void playlist_free(playlist_t *playlist)
playlist_free_entry(entry);
}
free(playlist->entries);
playlist->entries = NULL;
RBUF_FREE(playlist->entries);
}
free(playlist);
@ -1888,18 +1902,18 @@ void playlist_free(playlist_t *playlist)
**/
void playlist_clear(playlist_t *playlist)
{
size_t i;
size_t i, len;
if (!playlist)
return;
for (i = 0; i < playlist->size; i++)
for (i = 0, len = RBUF_LEN(playlist->entries); i < len; i++)
{
struct playlist_entry *entry = &playlist->entries[i];
if (entry)
playlist_free_entry(entry);
}
playlist->size = 0;
RBUF_CLEAR(playlist->entries);
}
/**
@ -1913,7 +1927,7 @@ size_t playlist_size(playlist_t *playlist)
{
if (!playlist)
return 0;
return playlist->size;
return RBUF_LEN(playlist->entries);
}
/**
@ -1993,8 +2007,19 @@ static JSON_Parser_HandlerResult JSONStartObjectHandler(JSON_Parser parser)
{
if ((pCtx->array_depth == 1) && !pCtx->capacity_exceeded)
{
if (pCtx->playlist->size < pCtx->playlist->config.capacity)
pCtx->current_entry = &pCtx->playlist->entries[pCtx->playlist->size];
size_t len = RBUF_LEN(pCtx->playlist->entries);
if (len < pCtx->playlist->config.capacity)
{
/* Allocate memory to fit one more item but don't resize the
* buffer just yet, wait until JSONEndObjectHandler for that */
if (!RBUF_TRYFIT(pCtx->playlist->entries, len + 1))
{
pCtx->out_of_memory = true;
return JSON_Parser_Abort;
}
pCtx->current_entry = &pCtx->playlist->entries[len];
memset(pCtx->current_entry, 0, sizeof(*pCtx->current_entry));
}
else
{
/* Hit max item limit.
@ -2022,7 +2047,8 @@ static JSON_Parser_HandlerResult JSONEndObjectHandler(JSON_Parser parser)
if (pCtx->in_items && pCtx->object_depth == 2)
{
if ((pCtx->array_depth == 1) && !pCtx->capacity_exceeded)
pCtx->playlist->size++;
RBUF_RESIZE(pCtx->playlist->entries,
RBUF_LEN(pCtx->playlist->entries) + 1);
}
retro_assert(pCtx->object_depth > 0);
@ -2281,6 +2307,8 @@ static bool playlist_read_file(playlist_t *playlist)
{
unsigned i;
int test_char;
bool res = true;
#if defined(HAVE_ZLIB)
/* Always use RZIP interface when reading playlists
* > this will automatically handle uncompressed
@ -2386,6 +2414,12 @@ static bool playlist_read_file(playlist_t *playlist)
if (!JSON_Parser_Parse(context.parser, chunk,
(size_t)length, JSON_False))
{
if (context.out_of_memory)
{
RARCH_WARN("Ran out of memory while parsing JSON playlist\n");
res = false;
goto json_cleanup;
}
/* Note: Chunk may not be null-terminated.
* It is therefore dangerous to print its contents.
* Setting a size limit here mitigates the issue, but
@ -2418,6 +2452,7 @@ json_cleanup:
}
else
{
size_t len = RBUF_LEN(playlist->entries);
char line_buf[PLAYLIST_ENTRIES][PATH_MAX_LENGTH] = {{0}};
/* Unnecessary, but harmless */
@ -2425,8 +2460,7 @@ json_cleanup:
line_buf[i][0] = '\0';
/* Read playlist entries */
playlist->size = 0;
while (playlist->size < playlist->config.capacity)
while (len < playlist->config.capacity)
{
size_t i;
size_t lines_read = 0;
@ -2454,11 +2488,17 @@ json_cleanup:
* is a valid playlist entry */
if (lines_read >= PLAYLIST_ENTRIES)
{
struct playlist_entry *entry =
&playlist->entries[playlist->size];
struct playlist_entry* entry;
if (!entry)
continue;
if (!RBUF_TRYFIT(playlist->entries, len + 1))
{
res = false; /* out of memory */
goto end;
}
RBUF_RESIZE(playlist->entries, len + 1);
entry = &playlist->entries[len++];
memset(entry, 0, sizeof(*entry));
/* path */
if (!string_is_empty(line_buf[0]))
@ -2483,8 +2523,6 @@ json_cleanup:
/* db_name */
if (!string_is_empty(line_buf[5]))
entry->db_name = strdup(line_buf[5]);
playlist->size++;
}
/* If fewer than 'PLAYLIST_ENTRIES' lines were
* read, then this is metadata */
@ -2611,7 +2649,7 @@ json_cleanup:
end:
intfstream_close(file);
free(file);
return true;
return res;
}
void playlist_free_cached(void)
@ -2658,18 +2696,8 @@ bool playlist_init_cached(const playlist_config_t *config)
**/
playlist_t *playlist_init(const playlist_config_t *config)
{
struct playlist_entry *entries = NULL;
playlist_t *playlist = (playlist_t*)malloc(sizeof(*playlist));
/* Cache configuration parameters */
if (!playlist || !playlist_config_copy(config, &playlist->config))
goto error;
/* Create entries array */
entries = (struct playlist_entry*)calloc(
playlist->config.capacity, sizeof(*entries));
if (!entries)
if (!playlist)
goto error;
/* Set initial values */
@ -2677,18 +2705,22 @@ playlist_t *playlist_init(const playlist_config_t *config)
playlist->old_format = false;
playlist->compressed = false;
playlist->cached_external = false;
playlist->size = 0;
playlist->default_core_name = NULL;
playlist->default_core_path = NULL;
playlist->base_content_directory = NULL;
playlist->entries = entries;
playlist->entries = NULL;
playlist->label_display_mode = LABEL_DISPLAY_MODE_DEFAULT;
playlist->right_thumbnail_mode = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
playlist->left_thumbnail_mode = PLAYLIST_THUMBNAIL_MODE_DEFAULT;
playlist->sort_mode = PLAYLIST_SORT_MODE_DEFAULT;
/* Cache configuration parameters */
if (!playlist_config_copy(config, &playlist->config))
goto error;
/* Attempt to read any existing playlist file */
playlist_read_file(playlist);
if (!playlist_read_file(playlist))
goto error;
/* Try auto-fixing paths if enabled, and playlist
* base content directory is different */
@ -2697,11 +2729,10 @@ playlist_t *playlist_init(const playlist_config_t *config)
!string_is_equal(playlist->base_content_directory,
playlist->config.base_content_directory))
{
size_t i;
size_t j;
size_t i, j, len;
char tmp_entry_path[PATH_MAX_LENGTH];
for (i = 0; i < playlist->size; i++)
for (i = 0, len = RBUF_LEN(playlist->entries); i < len; i++)
{
struct playlist_entry *entry = &playlist->entries[i];
@ -2847,10 +2878,11 @@ void playlist_qsort(playlist_t *playlist)
/* Avoid inadvertent sorting if 'sort mode'
* has been set explicitly to PLAYLIST_SORT_MODE_OFF */
if (!playlist ||
(playlist->sort_mode == PLAYLIST_SORT_MODE_OFF))
(playlist->sort_mode == PLAYLIST_SORT_MODE_OFF) ||
!playlist->entries)
return;
qsort(playlist->entries, playlist->size,
qsort(playlist->entries, RBUF_LEN(playlist->entries),
sizeof(struct playlist_entry),
(int (*)(const void *, const void *))playlist_qsort_func);
}
@ -2890,7 +2922,7 @@ bool playlist_index_is_valid(playlist_t *playlist, size_t idx,
if (!playlist)
return false;
if (idx >= playlist->size)
if (idx >= RBUF_LEN(playlist->entries))
return false;
return string_is_equal(playlist->entries[idx].path, path) &&
@ -2944,7 +2976,7 @@ bool playlist_entries_are_equal(
void playlist_get_crc32(playlist_t *playlist, size_t idx,
const char **crc32)
{
if (!playlist)
if (!playlist || idx >= RBUF_LEN(playlist->entries))
return;
if (crc32)
@ -2954,7 +2986,7 @@ void playlist_get_crc32(playlist_t *playlist, size_t idx,
void playlist_get_db_name(playlist_t *playlist, size_t idx,
const char **db_name)
{
if (!playlist)
if (!playlist || idx >= RBUF_LEN(playlist->entries))
return;
if (db_name)

View file

@ -29,7 +29,7 @@
RETRO_BEGIN_DECLS
/* Default maximum playlist size */
#define COLLECTION_SIZE 99999
#define COLLECTION_SIZE 0x7FFFFFFF
typedef struct content_playlist playlist_t;