Skip to content

Commit 28386c7

Browse files
committed
(config_file) Memory allocation savings and performance improvements:
1. config_file_parse_line key parsing — Saves 1 malloc + up to N reallocs per config line 2. config_file_extract_value empty value — Saves 1 byte per empty-value entry. 3. config_file_load_internal deferred alloc — Saves 1 malloc + 1 free for every blank line, empty line, and comment line in a config file. 4. config_file_from_string_internal deferred alloc — Same saving as #3 but for the string-parsing path. Same magnitude. 5. config_file_add_child_list tail — No memory saving, but avoids an O(n) linked-list walk per #include merge by using the already-tracked tail pointer. Also eliminates duplicated readonly-loop code. 6. config_file_write stack setvbuf — Saves 1 calloc(16384) + 1 free per config write. That's 16 KB of heap allocation eliminated on every save, replaced with a stack-local buffer that costs zero allocator overhead. 7. strtol instead of strtod — No memory saving; avoids unnecessary FPU work when parsing integer arrays.
1 parent cb58dbc commit 28386c7

2 files changed

Lines changed: 89 additions & 103 deletions

File tree

libretro-common/file/config_file.c

Lines changed: 79 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,6 @@ static char *config_file_strip_comment(char *str)
207207

208208
static char *config_file_extract_value(char *line)
209209
{
210-
char *dst = NULL;
211210
while (ISSPACE((int)*line))
212211
line++;
213212

@@ -255,13 +254,9 @@ static char *config_file_extract_value(char *line)
255254
return strdup(value);
256255
}
257256

258-
/* Note 2: This is an unrolled strldup call
259-
* to avoid an unnecessary dependency -
260-
* call is strldup("", sizeof(""))
261-
**/
262-
dst = (char*)malloc(sizeof(char) * 2);
263-
strlcpy(dst, "", 1);
264-
return dst;
257+
/* Note 2: Return an empty string.
258+
* calloc gives us a NUL-terminated empty string in one call. */
259+
return (char*)calloc(1, 1);
265260
}
266261

267262
/* Move semantics? */
@@ -271,32 +266,30 @@ static void config_file_add_child_list(config_file_t *parent,
271266
struct config_entry_list *list = child->entries;
272267
bool merge_hash_map = false;
273268

274-
if (parent->entries)
269+
/* set list readonly */
270+
while (list)
275271
{
276-
struct config_entry_list *head = parent->entries;
277-
while (head->next)
278-
head = head->next;
272+
list->readonly = true;
273+
list = list->next;
274+
}
279275

280-
/* set list readonly */
281-
while (list)
276+
if (parent->entries)
277+
{
278+
/* Use tracked tail instead of walking the list */
279+
if (parent->tail)
280+
parent->tail->next = child->entries;
281+
else
282282
{
283-
list->readonly = true;
284-
list = list->next;
283+
struct config_entry_list *head = parent->entries;
284+
while (head->next)
285+
head = head->next;
286+
head->next = child->entries;
285287
}
286-
head->next = child->entries;
287288

288289
merge_hash_map = true;
289290
}
290291
else
291-
{
292-
/* set list readonly */
293-
while (list)
294-
{
295-
list->readonly = true;
296-
list = list->next;
297-
}
298292
parent->entries = child->entries;
299-
}
300293

301294
/* Rebase tail. */
302295
if (parent->entries)
@@ -442,12 +435,21 @@ static int config_file_load_internal(
442435

443436
while (!filestream_eof(file))
444437
{
445-
char *line = NULL;
446-
struct config_entry_list *list = (struct config_entry_list*)
447-
malloc(sizeof(*list));
438+
struct config_entry_list *list = NULL;
439+
char *line = filestream_getline(file);
440+
441+
if (!line)
442+
continue;
443+
444+
if (string_is_empty(line))
445+
{
446+
free(line);
447+
continue;
448+
}
448449

449-
if (!list)
450+
if (!(list = (struct config_entry_list*)malloc(sizeof(*list))))
450451
{
452+
free(line);
451453
filestream_close(file);
452454
return -1;
453455
}
@@ -457,17 +459,7 @@ static int config_file_load_internal(
457459
list->value = NULL;
458460
list->next = NULL;
459461

460-
line = filestream_getline(file);
461-
462-
if (!line)
463-
{
464-
free(list);
465-
continue;
466-
}
467-
468-
if (
469-
!string_is_empty(line)
470-
&& config_file_parse_line(conf, list, line, cb))
462+
if (config_file_parse_line(conf, list, line, cb))
471463
{
472464
if (conf->entries)
473465
conf->tail->next = list;
@@ -492,11 +484,10 @@ static int config_file_load_internal(
492484
}
493485
}
494486
}
487+
else
488+
free(list);
495489

496490
free(line);
497-
498-
if (list != conf->tail)
499-
free(list);
500491
}
501492

502493
filestream_close(file);
@@ -507,10 +498,8 @@ static int config_file_load_internal(
507498
static bool config_file_parse_line(config_file_t *conf,
508499
struct config_entry_list *list, char *line, config_file_cb_t *cb)
509500
{
510-
size_t cur_size = 32;
511501
size_t idx = 0;
512502
char *key = NULL;
513-
char *key_tmp = NULL;
514503
/* Remove any comment text */
515504
char *comment = config_file_strip_comment(line);
516505

@@ -596,31 +585,23 @@ static bool config_file_parse_line(config_file_t *conf,
596585
while (ISSPACE((int)*line))
597586
line++;
598587

599-
/* Allocate storage for key */
600-
if (!(key = (char*)malloc(cur_size + 1)))
601-
return false;
602-
603-
/* Copy line contents into key until we
604-
* reach the next space character */
605-
while (isgraph((int)*line))
588+
/* Measure key length first (up to next non-graph char),
589+
* then copy once - avoids malloc+realloc growth pattern */
606590
{
607-
/* If current key storage is too small,
608-
* double its size */
609-
if (idx == cur_size)
610-
{
611-
cur_size *= 2;
612-
if (!(key_tmp = (char*)realloc(key, cur_size + 1)))
613-
{
614-
free(key);
615-
return false;
616-
}
591+
const char *key_start = line;
592+
while (isgraph((int)*line))
593+
line++;
594+
idx = (size_t)(line - key_start);
617595

618-
key = key_tmp;
619-
}
596+
if (idx == 0)
597+
return false;
620598

621-
key[idx++] = *line++;
599+
if (!(key = (char*)malloc(idx + 1)))
600+
return false;
601+
602+
memcpy(key, key_start, idx);
603+
key[idx] = '\0';
622604
}
623-
key[idx] = '\0';
624605

625606
/* Add key and value entries to list */
626607
list->key = key;
@@ -670,43 +651,45 @@ static int config_file_from_string_internal(
670651

671652
while (line)
672653
{
673-
struct config_entry_list *list = (struct config_entry_list*)
674-
malloc(sizeof(*list));
675-
676-
if (!list)
677-
return -1;
678-
679-
list->readonly = false;
680-
list->key = NULL;
681-
list->value = NULL;
682-
list->next = NULL;
654+
struct config_entry_list *list = NULL;
683655

684656
/* Parse current line */
685-
if (
686-
!string_is_empty(line)
687-
&& config_file_parse_line(conf, list, line, NULL))
657+
if (!string_is_empty(line))
688658
{
689-
if (conf->entries)
690-
conf->tail->next = list;
691-
else
692-
conf->entries = list;
659+
list = (struct config_entry_list*)
660+
malloc(sizeof(*list));
693661

694-
conf->tail = list;
662+
if (!list)
663+
return -1;
695664

696-
if (list->key)
665+
list->readonly = false;
666+
list->key = NULL;
667+
list->value = NULL;
668+
list->next = NULL;
669+
670+
if (config_file_parse_line(conf, list, line, NULL))
697671
{
698-
/* Only add entry to the map if an entry
699-
* with the specified value does not
700-
* already exist */
701-
uint32_t hash = rhmap_hash_string(list->key);
702-
if (!RHMAP_HAS_FULL(conf->entries_map, hash, list->key))
703-
RHMAP_SET_FULL(conf->entries_map, hash, list->key, list);
672+
if (conf->entries)
673+
conf->tail->next = list;
674+
else
675+
conf->entries = list;
676+
677+
conf->tail = list;
678+
679+
if (list->key)
680+
{
681+
/* Only add entry to the map if an entry
682+
* with the specified value does not
683+
* already exist */
684+
uint32_t hash = rhmap_hash_string(list->key);
685+
if (!RHMAP_HAS_FULL(conf->entries_map, hash, list->key))
686+
RHMAP_SET_FULL(conf->entries_map, hash, list->key, list);
687+
}
704688
}
689+
else
690+
free(list);
705691
}
706692

707-
if (list != conf->tail)
708-
free(list);
709-
710693
/* Get next line of config file */
711694
line = strtok_r(NULL, "\n", &save_ptr);
712695
}
@@ -1409,20 +1392,17 @@ bool config_file_write(config_file_t *conf, const char *path, bool sort)
14091392
config_file_dump(conf, stdout, sort);
14101393
else
14111394
{
1412-
void* buf = NULL;
1395+
char buf[0x4000];
14131396
FILE *file = (FILE*)fopen_utf8(path, "wb");
14141397
if (!file)
14151398
return false;
14161399

1417-
buf = calloc(1, 0x4000);
1418-
setvbuf(file, (char*)buf, _IOFBF, 0x4000);
1400+
setvbuf(file, buf, _IOFBF, sizeof(buf));
14191401

14201402
config_file_dump(conf, file, sort);
14211403

14221404
if (file != stdout)
14231405
fclose(file);
1424-
if (buf)
1425-
free(buf);
14261406

14271407
/* Only update modified flag if config file
14281408
* is actually written to disk */

libretro-common/file/config_file_userdata.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,12 @@ int config_userdata_get_float(void *userdata, const char *key_str,
3030
{
3131
char key[256];
3232
struct config_file_userdata *usr = (struct config_file_userdata*)userdata;
33+
34+
*value = default_value;
35+
3336
fill_pathname_join_delim(key, usr->prefix[0], key_str, '_', sizeof(key));
3437
if (config_get_float(usr->conf, key, value))
3538
return true;
36-
*value = default_value;
3739
fill_pathname_join_delim(key, usr->prefix[1], key_str, '_', sizeof(key));
3840
if (config_get_float(usr->conf, key, value))
3941
return true;
@@ -45,10 +47,12 @@ int config_userdata_get_int(void *userdata, const char *key_str,
4547
{
4648
char key[256];
4749
struct config_file_userdata *usr = (struct config_file_userdata*)userdata;
50+
51+
*value = default_value;
52+
4853
fill_pathname_join_delim(key, usr->prefix[0], key_str, '_', sizeof(key));
4954
if (config_get_int(usr->conf, key, value))
5055
return true;
51-
*value = default_value;
5256
fill_pathname_join_delim(key, usr->prefix[1], key_str, '_', sizeof(key));
5357
if (config_get_int(usr->conf, key, value))
5458
return true;
@@ -60,10 +64,12 @@ int config_userdata_get_hex(void *userdata, const char *key_str,
6064
{
6165
char key[256];
6266
struct config_file_userdata *usr = (struct config_file_userdata*)userdata;
67+
68+
*value = default_value;
69+
6370
fill_pathname_join_delim(key, usr->prefix[0], key_str, '_', sizeof(key));
6471
if (config_get_hex(usr->conf, key, value))
6572
return true;
66-
*value = default_value;
6773
fill_pathname_join_delim(key, usr->prefix[1], key_str, '_', sizeof(key));
6874
if (config_get_hex(usr->conf, key, value))
6975
return true;
@@ -122,7 +128,7 @@ int config_userdata_get_int_array(void *userdata, const char *key_str,
122128
string_split_noalloc(&list, str, " ");
123129
*values = (int*)calloc(list.size, sizeof(int));
124130
for (i = 0; i < list.size; i++)
125-
(*values)[i] = (int)strtod(list.elems[i].data, NULL);
131+
(*values)[i] = (int)strtol(list.elems[i].data, NULL, 0);
126132
*out_num_values = (unsigned)list.size;
127133
string_list_deinitialize(&list);
128134
free(str);

0 commit comments

Comments
 (0)