mirror of
https://github.com/thepigeongenerator/tetris_clone.git
synced 2025-12-17 05:55:46 +01:00
rewrite string accumilation to use a dynamic array instead of many reallocations
This commit is contained in:
114
src/game/opts.c
114
src/game/opts.c
@@ -3,6 +3,8 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -26,37 +28,55 @@ struct opts opts = {
|
|||||||
COLOUR8_BLUE, // colour for the J shape
|
COLOUR8_BLUE, // colour for the J shape
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* contains the basic structure for a string */
|
||||||
|
struct str {
|
||||||
|
char* dat; // pointer to the string data
|
||||||
|
unsigned cap; // current string capacity
|
||||||
|
};
|
||||||
|
|
||||||
struct proc_buf_dat {
|
struct proc_buf_dat {
|
||||||
|
struct str pkey; // contains a memory address for storing the key string, the memory stored grows via 2ˣ
|
||||||
|
struct str pval; // contains a memory address for storing the val string, the memory stored grows via 2ˣ
|
||||||
char const* restrict key; // the pointer for the start of the key
|
char const* restrict key; // the pointer for the start of the key
|
||||||
char const* restrict val; // the pointer for the start of the value
|
char const* restrict val; // the pointer for the start of the value
|
||||||
char* restrict pkey; // pointer to a malloc'd key
|
|
||||||
char* restrict pval; // pointer to a malloc'd val
|
|
||||||
unsigned key_len; // string length of the key data
|
|
||||||
unsigned val_len; // string length of the value data
|
|
||||||
unsigned pkey_len; // string length of the malloc'd key when they got malloc'd
|
unsigned pkey_len; // string length of the malloc'd key when they got malloc'd
|
||||||
unsigned pval_len; // string length of the malloc'd val when they got malloc'd
|
unsigned pval_len; // string length of the malloc'd val when they got malloc'd
|
||||||
|
unsigned key_len; // string length of the key data
|
||||||
|
unsigned val_len; // string length of the value data
|
||||||
bool feq; // whether we've found the equal sign and are looking for the value
|
bool feq; // whether we've found the equal sign and are looking for the value
|
||||||
bool eol; // whether we've reached the end of the line/string, so we don't read what should be comments as data, etc.
|
bool eol; // whether we've reached the end of the line/string, so we don't read what should be comments as data, etc.
|
||||||
bool nodat; // whether we are done and can just look for an EOL indicator
|
bool nodat; // whether we are done and can just look for an EOL indicator
|
||||||
};
|
};
|
||||||
|
|
||||||
/* use malloc to append src to the end of dest.
|
/* appends src onto dest.dat, if test.dat hasn't been allocated, or does not have enough space.
|
||||||
if *dest == NULL, memory is allocated, otherwise it is reallocated.
|
The size is the nearest 2ˣ rounded-up value of src_len.
|
||||||
returns 0 upon success, 1 upon failure. */
|
returns 0 upon success, 1 upon failure. */
|
||||||
__attribute__((nonnull(1, 3))) static inline int str_put(char* restrict* restrict dest, size_t dest_len, char const* restrict src, size_t src_len) {
|
__attribute__((nonnull(1, 3))) static inline int str_put(struct str* restrict dest, size_t dest_len, char const* restrict src, size_t src_len) {
|
||||||
if (!*dest) {
|
// check if (re)allocation needs to occur
|
||||||
*dest = malloc(src_len + 1);
|
if (dest->cap <= src_len) { // check for equality as well due to `\0`
|
||||||
if (!*dest) return 1;
|
dest->cap = src_len; // using src_len as-is, since it is string length, thus already one short.
|
||||||
} else {
|
for (unsigned i = 1; i < SIZE_WIDTH; i <<= 1) // then loop through each 2ˣ bit in size_t
|
||||||
assert(src_len > dest_len); // sanity check
|
dest->cap |= dest->cap >> i; // OR together the shifted result (shifted 1, 2, 4, 8, 16 on 32 bit systems)
|
||||||
void* ptr = realloc(*dest, src_len + 1);
|
dest->cap++; // round to the most significant bit (0111 -> 1000)
|
||||||
if (!ptr) return 1;
|
|
||||||
*dest = ptr;
|
// (re)allocate the array
|
||||||
|
if (!dest->dat) {
|
||||||
|
dest->dat = malloc(dest->cap); // allocate memory for this capacity
|
||||||
|
if (!dest->dat) return 1; // return 1 upon failure
|
||||||
|
} else {
|
||||||
|
void* ptr = realloc(dest->dat, dest->cap); // reallocate to the new capacity
|
||||||
|
if (!ptr) {
|
||||||
|
free(dest->dat); // free up resources by the old (still valid) pointer, and return failure
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
dest->dat = ptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// copy the missing data to the end of the destination
|
// copy the missing data to the end of the destination
|
||||||
memcpy(*dest + dest_len, src, src_len - dest_len);
|
memcpy(dest->dat + dest_len, src, src_len - dest_len);
|
||||||
(*dest)[src_len] = '\0'; // null-terminate the destination
|
dest->dat[src_len] = '\0'; // null-terminate the destination
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,9 +85,11 @@ __attribute__((nonnull(1, 3))) static inline int str_put(char* restrict* restric
|
|||||||
static void proc_buf(char const* restrict buf, struct proc_buf_dat* restrict dat) {
|
static void proc_buf(char const* restrict buf, struct proc_buf_dat* restrict dat) {
|
||||||
// reset if EOL has been reached
|
// reset if EOL has been reached
|
||||||
if (dat->eol) {
|
if (dat->eol) {
|
||||||
free(dat->pkey);
|
// zero-initialize all except the pointers; keep the pointers.
|
||||||
free(dat->pval);
|
*dat = (struct proc_buf_dat){
|
||||||
*dat = (struct proc_buf_dat){0};
|
.pkey = dat->pkey,
|
||||||
|
.pval = dat->pval,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// loop through each character
|
// loop through each character
|
||||||
@@ -103,10 +125,12 @@ static void proc_buf(char const* restrict buf, struct proc_buf_dat* restrict dat
|
|||||||
|
|
||||||
// allocate memory for the resulting string(s)
|
// allocate memory for the resulting string(s)
|
||||||
if (dat->pval_len < dat->val_len) { // first check this condition, as key won't have changed if this is true
|
if (dat->pval_len < dat->val_len) { // first check this condition, as key won't have changed if this is true
|
||||||
str_put(&dat->pval, dat->pval_len, dat->val, dat->val_len);
|
if (str_put(&dat->pval, dat->pval_len, dat->val, dat->val_len))
|
||||||
|
fatal(ERROR_STD_MEMORY_INIT, __FILE_NAME__, __LINE__, "something went wrong when attempting to allocate space for the option string");
|
||||||
dat->pval_len = dat->val_len;
|
dat->pval_len = dat->val_len;
|
||||||
} else if (dat->pkey_len < dat->key_len) {
|
} else if (dat->pkey_len < dat->key_len) {
|
||||||
str_put(&dat->pkey, dat->pkey_len, dat->key, dat->key_len);
|
if (str_put(&dat->pkey, dat->pkey_len, dat->key, dat->key_len))
|
||||||
|
fatal(ERROR_STD_MEMORY_INIT, __FILE_NAME__, __LINE__, "something went wrong when attempting to allocate space for the option string");
|
||||||
dat->pkey_len = dat->key_len;
|
dat->pkey_len = dat->key_len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,15 +150,24 @@ static void proc_buf(char const* restrict buf, struct proc_buf_dat* restrict dat
|
|||||||
/* attempts to load the options,
|
/* attempts to load the options,
|
||||||
returns 1 upon failure */
|
returns 1 upon failure */
|
||||||
int load_opts(void) {
|
int load_opts(void) {
|
||||||
if (!path_opts) return 1;
|
if (!path_opts) {
|
||||||
|
error("the variable to the path to the options failed to initialize");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (faccess(path_opts, FA_R)) {
|
if (faccess(path_opts, FA_R)) {
|
||||||
error("attempted to load opts file, but the given path wasn't readable");
|
warn("attempted to load '%s', but the given path wasn't readable or doesn't exist", path_opts);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// open the file in read mode
|
// open the file in read mode
|
||||||
FILE* fptr = fopen(path_opts, "r");
|
FILE* fptr = fopen(path_opts, "r");
|
||||||
if (!fptr) return 1;
|
if (!fptr) {
|
||||||
|
error("failed to open '%s'", path_opts);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("loading options configuration from '%s'...", path_opts);
|
||||||
|
|
||||||
struct proc_buf_dat dat = {0};
|
struct proc_buf_dat dat = {0};
|
||||||
|
|
||||||
@@ -143,22 +176,23 @@ int load_opts(void) {
|
|||||||
while (fgets(buf, BUF_SIZE, fptr)) {
|
while (fgets(buf, BUF_SIZE, fptr)) {
|
||||||
// process the data in the current buffer
|
// process the data in the current buffer
|
||||||
proc_buf(buf, &dat);
|
proc_buf(buf, &dat);
|
||||||
if (dat.pkey) debug("key: '%s'", dat.pkey);
|
if (dat.pkey.dat) debug("key: '%s'", dat.pkey.dat);
|
||||||
if (dat.pval) debug("val: '%s'", dat.pval);
|
if (dat.pval.dat) debug("val: '%s'", dat.pval.dat);
|
||||||
|
|
||||||
// ignore if EOL hasn't been reached yet, due to the built up data possibly being incomplete
|
// ignore if EOL hasn't been reached yet, due to the built up data possibly being incomplete
|
||||||
if (!dat.eol) continue;
|
if (!dat.eol) continue;
|
||||||
if (!dat.pkey || !dat.pval) continue;
|
|
||||||
else if (!!strcmp(dat.pkey, "sensitivity")) load_opt(float, opts.sensitivity, dat.pkey, dat.pval, strtof32, );
|
if (!dat.pkey.dat || !dat.pval.dat) continue;
|
||||||
else if (!!strcmp(dat.pkey, "sensitivity_roll")) load_opt(float, opts.sensitivity_roll, dat.pkey, dat.pval, strtof32, );
|
else if (!!strcmp(dat.pkey.dat, "sensitivity")) load_opt(float, opts.sensitivity, dat.pkey.dat, dat.pval.dat, strtof32, );
|
||||||
else if (!!strcmp(dat.pkey, "colour_O")) load_opt(colour8, opts.colour_O, dat.pkey, dat.pval, strtol, , 16);
|
else if (!!strcmp(dat.pkey.dat, "sensitivity_roll")) load_opt(float, opts.sensitivity_roll, dat.pkey.dat, dat.pval.dat, strtof32, );
|
||||||
else if (!!strcmp(dat.pkey, "colour_I")) load_opt(colour8, opts.colour_I, dat.pkey, dat.pval, strtol, , 16);
|
else if (!!strcmp(dat.pkey.dat, "colour_O")) load_opt(colour8, opts.colour_O, dat.pkey.dat, dat.pval.dat, strtol, , 16);
|
||||||
else if (!!strcmp(dat.pkey, "colour_S")) load_opt(colour8, opts.colour_S, dat.pkey, dat.pval, strtol, , 16);
|
else if (!!strcmp(dat.pkey.dat, "colour_I")) load_opt(colour8, opts.colour_I, dat.pkey.dat, dat.pval.dat, strtol, , 16);
|
||||||
else if (!!strcmp(dat.pkey, "colour_Z")) load_opt(colour8, opts.colour_Z, dat.pkey, dat.pval, strtol, , 16);
|
else if (!!strcmp(dat.pkey.dat, "colour_S")) load_opt(colour8, opts.colour_S, dat.pkey.dat, dat.pval.dat, strtol, , 16);
|
||||||
else if (!!strcmp(dat.pkey, "colour_T")) load_opt(colour8, opts.colour_T, dat.pkey, dat.pval, strtol, , 16);
|
else if (!!strcmp(dat.pkey.dat, "colour_Z")) load_opt(colour8, opts.colour_Z, dat.pkey.dat, dat.pval.dat, strtol, , 16);
|
||||||
else if (!!strcmp(dat.pkey, "colour_L")) load_opt(colour8, opts.colour_L, dat.pkey, dat.pval, strtol, , 16);
|
else if (!!strcmp(dat.pkey.dat, "colour_T")) load_opt(colour8, opts.colour_T, dat.pkey.dat, dat.pval.dat, strtol, , 16);
|
||||||
else if (!!strcmp(dat.pkey, "colour_J")) load_opt(colour8, opts.colour_J, dat.pkey, dat.pval, strtol, , 16);
|
else if (!!strcmp(dat.pkey.dat, "colour_L")) load_opt(colour8, opts.colour_L, dat.pkey.dat, dat.pval.dat, strtol, , 16);
|
||||||
else error("unknown key-value pair: '%s = %s'", dat.pkey, dat.pval);
|
else if (!!strcmp(dat.pkey.dat, "colour_J")) load_opt(colour8, opts.colour_J, dat.pkey.dat, dat.pval.dat, strtol, , 16);
|
||||||
|
else error("unknown key-value pair: '%s = %s'", dat.pkey.dat, dat.pval.dat);
|
||||||
// TODO: load opts from the file
|
// TODO: load opts from the file
|
||||||
// the options shall be loaded as key-value-pairs
|
// the options shall be loaded as key-value-pairs
|
||||||
// lines starting with # are seen as comments
|
// lines starting with # are seen as comments
|
||||||
@@ -166,8 +200,8 @@ int load_opts(void) {
|
|||||||
// if a field couldn't be parsed the program is not allowed to fail. I'd say we fix the field for the user.
|
// if a field couldn't be parsed the program is not allowed to fail. I'd say we fix the field for the user.
|
||||||
}
|
}
|
||||||
|
|
||||||
free(dat.pkey);
|
free(dat.pkey.dat);
|
||||||
free(dat.pval);
|
free(dat.pval.dat);
|
||||||
fclose(fptr);
|
fclose(fptr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user