mirror of
https://github.com/libretro/RetroArch.git
synced 2025-02-07 09:57:49 +00:00
Change playlist to use dynamic array
Instead of initial 12MB memory allocation, use a dynamically growing array. This shares the container structure (now called rbuf) with the explore menu.
This commit is contained in:
parent
332505db86
commit
0a67c0416a
107
libretro-common/include/array/rbuf.h
Normal file
107
libretro-common/include/array/rbuf.h
Normal 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)))
|
||||
|
||||
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
|
@ -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,8 @@ 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]);
|
||||
RBUF_RESIZE(state->icons, system_count);
|
||||
|
||||
fill_pathname_application_special(path, sizeof(path),
|
||||
APPLICATION_SPECIAL_DIRECTORY_ASSETS_SYSICONS);
|
||||
@ -689,8 +652,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 +678,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 +804,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 +833,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 +852,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 +1048,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 +1063,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 +1096,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 +1186,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 +1270,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 +1311,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;
|
||||
|
201
playlist.c
201
playlist.c
@ -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];
|
||||
|
||||
@ -2850,7 +2881,7 @@ void playlist_qsort(playlist_t *playlist)
|
||||
(playlist->sort_mode == PLAYLIST_SORT_MODE_OFF))
|
||||
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 +2921,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) &&
|
||||
|
Loading…
x
Reference in New Issue
Block a user