memory: use custom memory allocation wrappers

Introduces wrappers around memory allocation functions in `memory.h`
that should be used instead of the standard C ones.

These never return NULL and, with the exception of `mem_realloc()`,
zero-initialize the allocated memory like `calloc()` does.

All allocations made with the memory.h API must be deallocated with
`mem_free()`. Although standard `free()` will work on some platforms,
it's not portable (currently it won't work on Windows). Likewise,
`mem_free()` must not be used to free foreign allocations.

The standard C allocation functions are now diagnosed as deprecated.
They are, however, available with the `libc_` prefix in case interfacing
with foreign APIs is required. So far they are only used to implement
`memory.h`.

Perhaps the most important change is the introduction of the `ALLOC()`,
`ALLOC_ARRAY()`, and `ALLOC_FLEX()` macros. They take a type as a
parameter, and allocate enough memory with the correct alignment for
that type. That includes overaligned types as well. In most
circumstances you should prefer to use these macros. See the `memory.h`
header for some usage examples.
This commit is contained in:
Andrei Alexeyev 2023-01-09 04:19:31 +01:00
parent 8a3a82c7ed
commit b6978178b1
No known key found for this signature in database
GPG key ID: 72D26128040B9690
117 changed files with 1066 additions and 756 deletions

View file

@ -284,6 +284,10 @@ endif
config.set('TAISEI_BUILDCONF_HAVE_BUILTIN_POPCOUNTLL', cc.has_function('__builtin_popcountll'))
config.set('TAISEI_BUILDCONF_HAVE_BUILTIN_POPCOUNT', cc.has_function('__builtin_popcount'))
config.set('TAISEI_BUILDCONF_HAVE_BUILTIN_AVAILABLE', cc.has_function('__builtin_available'))
config.set('TAISEI_BUILDCONF_HAVE_ALIGNED_ALLOC', cc.has_function('aligned_alloc'))
config.set('TAISEI_BUILDCONF_HAVE_POSIX_MEMALIGN', cc.has_function('posix_memalign'))
config.set('TAISEI_BUILDCONF_HAVE_ALIGNED_MALLOC_FREE',
cc.has_function('_aligned_malloc') and cc.has_function('_aligned_free'))
if dep_zip.found()
if dep_zip.type_name() == 'internal'

View file

@ -46,7 +46,7 @@ static void aniplayer_reset(AniPlayer *plr, bool hard) {
}
AniQueueEntry *aniplayer_queue(AniPlayer *plr, const char *seqname, int loops) {
AniQueueEntry *s = calloc(1, sizeof(AniQueueEntry));
auto s = ALLOC(AniQueueEntry);
alist_append(&plr->queue, s);
plr->queuesize++;
@ -82,7 +82,7 @@ void aniplayer_update(AniPlayer *plr) {
s->clock++;
// The last condition assures that animations only switch at their end points
if(s->clock >= s->duration && plr->queuesize > 1 && s->clock%s->sequence->length == 0) {
free(alist_pop(&plr->queue));
mem_free(alist_pop(&plr->queue));
plr->queuesize--;
}
}

View file

@ -133,7 +133,7 @@ void audio_init(void) {
if(LIKELY(have_chans)) {
int num_chans = audio.sfx_chan_last - audio.sfx_chan_first + 1;
assume(num_chans > 0);
audio.chan_play_ids = calloc(num_chans, sizeof(*audio.chan_play_ids));
audio.chan_play_ids = ALLOC_ARRAY(num_chans, typeof(*audio.chan_play_ids));
}
}
@ -163,14 +163,12 @@ SFX *audio_sfx_load(const char *name, const char *path) {
double vol = ht_get(&audio.sfx_volumes, name, DEFAULT_SFX_VOLUME) / 128.0;
B.object.sfx.set_volume(impl, vol);
SFX *sfx = calloc(1, sizeof(*sfx));
sfx->impl = impl;
return sfx;
return ALLOC(SFX, { .impl = impl });
}
void audio_sfx_destroy(SFX *sfx) {
B.sfx_unload(sfx->impl);
free(sfx);
mem_free(sfx);
}
static bool is_skip_mode(void) {
@ -252,12 +250,12 @@ static SFXPlayID play_sound_internal(
}
if(delay > 0) {
struct enqueued_sound *s = malloc(sizeof(struct enqueued_sound));
s->time = global.frames + delay;
s->name = strdup(name);
s->cooldown = cooldown;
s->replace = replace;
list_push(&audio.sound_queue, s);
list_push(&audio.sound_queue, ALLOC(struct enqueued_sound, {
.time = global.frames + delay,
.name = strdup(name),
.cooldown = cooldown,
.replace = replace,
}));
return 0;
}
@ -286,8 +284,8 @@ static SFXPlayID play_sound_internal(
static void *discard_enqueued_sound(List **queue, List *vsnd, void *arg) {
struct enqueued_sound *snd = (struct enqueued_sound*)vsnd;
free(snd->name);
free(list_unlink(queue, vsnd));
mem_free(snd->name);
mem_free(list_unlink(queue, vsnd));
return NULL;
}
@ -296,8 +294,8 @@ static void *play_enqueued_sound(struct enqueued_sound **queue, struct enqueued_
play_sound_internal(snd->name, false, snd->cooldown, snd->replace, 0);
}
free(snd->name);
free(list_unlink(queue, snd));
mem_free(snd->name);
mem_free(list_unlink(queue, snd));
return NULL;
}

View file

@ -115,11 +115,11 @@ static bool audio_sdl_init(void) {
AudioStreamSpec mspec = astream_spec(aspec.format, aspec.channels, aspec.freq);
audio.mixer = calloc(1, sizeof(*audio.mixer));
audio.mixer = ALLOC(typeof(*audio.mixer));
if(!mixer_init(audio.mixer, &mspec)) {
mixer_shutdown(audio.mixer);
free(audio.mixer);
mem_free(audio.mixer);
SDL_CloseAudioDevice(audio.device);
SDL_QuitSubSystem(SDL_INIT_AUDIO);
return false;
@ -136,7 +136,7 @@ static bool audio_sdl_shutdown(void) {
SDL_CloseAudioDevice(audio.device);
SDL_QuitSubSystem(SDL_INIT_AUDIO);
mixer_shutdown(audio.mixer);
free(audio.mixer);
mem_free(audio.mixer);
log_info("Audio subsystem deinitialized (SDL)");
return true;

View file

@ -338,11 +338,11 @@ MixerBGMImpl *mixerbgm_load(const char *vfspath) {
return NULL;
}
MixerBGMImpl *bgm = calloc(1, sizeof(*bgm));
auto bgm = ALLOC(MixerBGMImpl);
if(!astream_open(&bgm->stream, rw, vfspath)) {
SDL_RWclose(rw);
free(bgm);
mem_free(bgm);
return NULL;
}
@ -352,7 +352,7 @@ MixerBGMImpl *mixerbgm_load(const char *vfspath) {
void mixerbgm_unload(MixerBGMImpl *bgm) {
astream_close(&bgm->stream);
free(bgm);
mem_free(bgm);
}
void mixer_notify_bgm_unload(Mixer *mx, MixerBGMImpl *bgm) {
@ -398,13 +398,13 @@ MixerSFXImpl *mixersfx_load(const char *vfspath, const AudioStreamSpec *spec) {
assert(pcm_size <= INT32_MAX);
MixerSFXImpl *isnd = calloc(1, sizeof(*isnd) + pcm_size);
auto isnd = ALLOC_FLEX(MixerSFXImpl, pcm_size);
bool ok = astream_crystalize(&stream, spec, pcm_size, &isnd->pcm);
astream_close(&stream);
if(!ok) {
free(isnd);
mem_free(isnd);
return NULL;
}
@ -415,7 +415,7 @@ MixerSFXImpl *mixersfx_load(const char *vfspath, const AudioStreamSpec *spec) {
}
void mixersfx_unload(MixerSFXImpl *sfx) {
free(sfx);
mem_free(sfx);
}
void mixer_notify_sfx_unload(Mixer *mx, MixerSFXImpl *sfx) {

View file

@ -42,7 +42,7 @@ bool splayer_init(StreamPlayer *plr, int num_channels, const AudioStreamSpec *ds
assert(dst_spec->frame_size == sizeof(struct stereo_frame));
plr->num_channels = num_channels,
plr->channels = calloc(sizeof(*plr->channels), num_channels);
plr->channels = ALLOC_ARRAY(num_channels, typeof(*plr->channels));
plr->dst_spec = *dst_spec;
for(int i = 0; i < num_channels; ++i) {
@ -80,7 +80,7 @@ void splayer_shutdown(StreamPlayer *plr) {
free_channel(plr->channels + i);
}
free(plr->channels);
mem_free(plr->channels);
}
static inline void splayer_stream_ended(StreamPlayer *plr, int chan) {

View file

@ -54,7 +54,7 @@ static ssize_t astream_pcm_tell(AudioStream *stream) {
static void astream_pcm_free(AudioStream *stream) {
PCMStreamContext *ctx = NOT_NULL(stream->opaque);
free(ctx);
mem_free(ctx);
}
static void astream_pcm_static_close(AudioStream *stream) {
@ -80,10 +80,10 @@ static AudioStreamProcs astream_pcm_static_procs = {
bool astream_pcm_open(AudioStream *stream, const AudioStreamSpec *spec, size_t pcm_buffer_size, void *pcm_buffer, int32_t loop_start) {
stream->procs = &astream_pcm_procs;
stream->opaque = calloc(1, sizeof(PCMStreamContext));
stream->opaque = ALLOC(PCMStreamContext);
if(!astream_pcm_reopen(stream, spec, pcm_buffer_size, pcm_buffer, loop_start)) {
free(stream->opaque);
mem_free(stream->opaque);
return false;
}

View file

@ -1346,7 +1346,7 @@ void boss_death(Boss **boss) {
static void free_attack(Attack *a) {
COEVENT_CANCEL_ARRAY(a->events);
free(a->name);
mem_free(a->name);
}
void free_boss(Boss *boss) {
@ -1359,7 +1359,7 @@ void free_boss(Boss *boss) {
ent_unregister(&boss->ent);
boss_set_portrait(boss, NULL, NULL, NULL);
aniplayer_free(&boss->ani);
free(boss->name);
mem_free(boss->name);
objpool_release(stage_object_pools.bosses, boss);
}

View file

@ -289,8 +289,8 @@ int cli_args(int argc, char **argv, CLIAction *a) {
}
void free_cli_action(CLIAction *a) {
free(a->filename);
mem_free(a->filename);
a->filename = NULL;
free(a->out_replay);
mem_free(a->out_replay);
a->out_replay = NULL;
}

View file

@ -106,7 +106,7 @@ static int config_compare_values(ConfigEntryType type, ConfigValue val0, ConfigV
static void config_free_value(ConfigEntryType type, ConfigValue *val) {
switch(type) {
case CONFIG_TYPE_STRING:
free(val->s);
mem_free(val->s);
val->s = NULL;
break;
@ -239,9 +239,8 @@ static ConfigEntry* config_get_unknown_entry(const char *name) {
}
}
l = malloc(sizeof(ConfigEntryList));
l = ALLOC(typeof(*l));
e = &l->entry;
memset(e, 0, sizeof(ConfigEntry));
stralloc(&e->name, name);
e->type = CONFIG_TYPE_STRING;
list_push(&unknowndefs, l);
@ -256,9 +255,9 @@ static void config_set_unknown(const char *name, const char *val) {
static void* config_delete_unknown_entry(List **list, List *lentry, void *arg) {
ConfigEntry *e = &((ConfigEntryList*)lentry)->entry;
free(e->name);
free(e->val.s);
free(list_unlink(list, lentry));
mem_free(e->name);
mem_free(e->val.s);
mem_free(list_unlink(list, lentry));
return NULL;
}
@ -313,7 +312,7 @@ void config_save(void) {
char *sp = vfs_repr(CONFIG_FILE, true);
log_info("Saved config '%s'", sp);
free(sp);
mem_free(sp);
}
#define INTOF(s) ((int)strtol(s, NULL, 10))
@ -447,7 +446,7 @@ void config_load(void) {
if(config_file) {
log_info("Loading configuration from %s", config_path);
ConfigParseState *state = malloc(sizeof(ConfigParseState));
auto state = ALLOC(ConfigParseState);
state->first_entry = -1;
if(!parse_keyvalue_stream_cb(config_file, config_set, state)) {
@ -459,7 +458,7 @@ void config_load(void) {
config_set_int(CONFIG_VERSION, 0);
}
free(state);
mem_free(state);
SDL_RWclose(config_file);
config_apply_upgrades(config_get_int(CONFIG_VERSION));
@ -473,7 +472,7 @@ void config_load(void) {
}
}
free(config_path);
mem_free(config_path);
// set config version to the latest
config_set_int(CONFIG_VERSION, sizeof(config_upgrades) / sizeof(ConfigUpgradeFunc));

View file

@ -158,7 +158,7 @@ void cotask_global_init(void) {
void cotask_global_shutdown(void) {
for(CoTask *task; (task = alist_pop(&task_pool));) {
koishi_deinit(&task->ko);
free(task);
mem_free(task);
}
}
@ -206,7 +206,7 @@ CoTask *cotask_new_internal(koishi_entrypoint_t entry_point) {
STAT_VAL(num_tasks_allocated), STAT_VAL(num_tasks_in_use)
);
} else {
task = calloc(1, sizeof(*task));
task = ALLOC(typeof(*task));
koishi_init(&task->ko, CO_STACK_SIZE, entry_point);
STAT_VAL_ADD(num_tasks_allocated, 1);
TASK_DEBUG(
@ -325,7 +325,7 @@ static bool cotask_finalize(CoTask *task) {
CoTaskHeapMemChunk *heap_alloc = task_data->mem.onheap_alloc_head;
while(heap_alloc) {
CoTaskHeapMemChunk *next = heap_alloc->next;
free(heap_alloc);
mem_free(heap_alloc);
heap_alloc = next;
}
@ -613,7 +613,7 @@ static void *_cotask_malloc(CoTaskData *task_data, size_t size, bool allow_heap_
}
log_warn("Requested size=%zu, available=%zi, serving from the heap", size, (ssize_t)available_on_stack);
CoTaskHeapMemChunk *chunk = calloc(1, sizeof(*chunk) + size);
auto chunk = ALLOC_FLEX(CoTaskHeapMemChunk, size);
chunk->next = task_data->mem.onheap_alloc_head;
task_data->mem.onheap_alloc_head = chunk;
mem = chunk->data;

View file

@ -201,13 +201,12 @@ static void credits_add(char *data, int time) {
for(c = data; *c; ++c)
if(*c == '\n') e->lines++;
e->data = malloc(e->lines * sizeof(char*));
e->data = ALLOC_ARRAY(e->lines, typeof(*e->data));
for(c = data; *c; ++c) {
if(*c == '\n') {
buf[i] = 0;
e->data[l] = malloc(strlen(buf) + 1);
strcpy(e->data[l], buf);
e->data[l] = strdup(buf);
i = 0;
++l;
} else {
@ -216,8 +215,7 @@ static void credits_add(char *data, int time) {
}
buf[i] = 0;
e->data[l] = malloc(strlen(buf) + 1);
strcpy(e->data[l], buf);
e->data[l] = strdup(buf);
credits.end += time;
}
@ -546,9 +544,9 @@ static void credits_free(void) {
dynarray_foreach_elem(&credits.entries, CreditsEntry *e, {
for(int i = 0; i < e->lines; ++i) {
free(e->data[i]);
mem_free(e->data[i]);
}
free(e->data);
mem_free(e->data);
});
dynarray_free_data(&credits.entries);

View file

@ -151,11 +151,11 @@ static void cutscene_advance(CutsceneState *st) {
}
if(st->text_entry && st->text_entry->text) {
CutsceneTextVisual *tv = calloc(1, sizeof(*tv));
tv->alpha = 0.1;
tv->target_alpha = 1;
tv->entry = st->text_entry;
alist_append(&st->text_visuals, tv);
alist_append(&st->text_visuals, ALLOC(CutsceneTextVisual, {
.alpha = 0.1,
.target_alpha = 1,
.entry = st->text_entry,
}));
}
}
@ -214,7 +214,7 @@ static LogicFrameAction cutscene_logic_frame(void *ctx) {
}
if(fapproach_p(&tv->alpha, tv->target_alpha, rate) == 0) {
free(alist_unlink(&st->text_visuals, tv));
mem_free(alist_unlink(&st->text_visuals, tv));
}
}
@ -395,13 +395,13 @@ static void cutscene_end_loop(void *ctx) {
for(CutsceneTextVisual *tv = st->text_visuals.first, *next; tv; tv = next) {
next = tv->next;
free(tv);
mem_free(tv);
}
fbmgr_group_destroy(st->mfb_group);
CallChain cc = st->cc;
free(st);
mem_free(st);
run_call_chain(&cc, NULL);
}
@ -415,13 +415,13 @@ static void cutscene_preload(const CutscenePhase phases[]) {
static CutsceneState *cutscene_state_new(const CutscenePhase phases[]) {
cutscene_preload(phases);
CutsceneState *st = calloc(1, sizeof(*st));
st->phase = &phases[0];
auto st = ALLOC(CutsceneState, {
.phase = &phases[0],
.mfb_group = fbmgr_group_create(),
});
switch_bg(st, st->phase->background);
reset_timers(st);
st->mfb_group = fbmgr_group_create();
FBAttachmentConfig a = { 0 };
a.attachment = FRAMEBUFFER_ATTACH_COLOR0;
a.tex_params.type = TEX_TYPE_RGBA_8;

View file

@ -21,7 +21,7 @@
#endif
void _dynarray_free_data(dynarray_size_t sizeof_element, DynamicArray *darr) {
free(darr->data);
mem_free(darr->data);
if(darr->capacity) {
DYNARRAY_DEBUG(darr, "%u/%u", darr->num_elements, darr->capacity);
}
@ -33,7 +33,7 @@ INLINE void _dynarray_resize(dynarray_size_t sizeof_element, DynamicArray *darr,
assert(capacity > 0);
DYNARRAY_DEBUG(darr, "capacity change: %u --> %u", darr->capacity, capacity);
darr->capacity = capacity;
darr->data = realloc(darr->data, sizeof_element * capacity);
darr->data = mem_realloc(darr->data, sizeof_element * capacity);
}
void _dynarray_ensure_capacity(dynarray_size_t sizeof_element, DynamicArray *darr, dynarray_size_t capacity) {

View file

@ -34,7 +34,7 @@ static struct {
} entities;
static void add_hook(EntityDrawHookList *list, EntityDrawHookCallback cb, void *arg) {
EntityDrawHook *hook = calloc(1, sizeof(*hook));
auto hook = ALLOC(EntityDrawHook);
hook->callback = cb;
hook->arg = arg;
@ -45,7 +45,7 @@ static void remove_hook(EntityDrawHookList *list, EntityDrawHookCallback cb) {
for(EntityDrawHook *hook = list->first; hook; hook = hook->next) {
if(hook->callback == cb) {
alist_unlink(list, hook);
free(hook);
mem_free(hook);
return;
}
}

View file

@ -155,7 +155,7 @@ static DirWatch *dirwatch_get(const char *path, bool create) {
return NULL;
}
dw = calloc(1, sizeof(*dw));
dw = ALLOC(typeof(*dw));
dw->path = strdup(path);
dw->wd = wd;
@ -174,14 +174,14 @@ static void dirwatch_delete(DirWatch *dw) {
WDRecord *wdrec = NOT_NULL(wdrecord_get(dw->wd, false));
list_unlink(&wdrec->dirwatch_list, dw);
ht_unset(&FW.dirpath_to_dirwatch, dw->path);
free(dw->path);
mem_free(dw->path);
if(wdrecord_decref(wdrec) == 0) {
inotify_rm_watch(FW.inotify, dw->wd);
wdrecord_delete(dw->wd);
}
free(dw);
mem_free(dw);
}
static void dirwatch_invalidate(DirWatch *dw) {
@ -257,7 +257,7 @@ void filewatch_init(void) {
return;
}
_fw_globals = calloc(1, sizeof(*_fw_globals));
_fw_globals = ALLOC(typeof(*_fw_globals));
FW.inotify = inotify;
ht_filewatchset_create(&FW.updated_watches);
ht_int2wdrecord_create(&FW.wd_records);
@ -286,7 +286,7 @@ void filewatch_shutdown(void) {
ht_destroy(&FW.dirpath_to_dirwatch);
SDL_DestroyMutex(FW.modify_mtx);
free(_fw_globals);
mem_free(_fw_globals);
_fw_globals = NULL;
}
}
@ -353,10 +353,10 @@ FileWatch *filewatch_watch(const char *syspath) {
return NULL;
}
FileWatch *fw = calloc(1, sizeof(*fw));
fw->dw = dw;
fw->filename = strdup(filename);
list_push(&dw->filewatch_list, fw);
auto fw = list_push(&dw->filewatch_list, ALLOC(FileWatch, {
.dw = dw,
.filename = strdup(filename),
}));
SDL_UnlockMutex(FW.modify_mtx);
@ -369,8 +369,8 @@ void filewatch_unwatch(FileWatch *fw) {
DirWatch *dw = NOT_NULL(fw->dw);
list_unlink(&dw->filewatch_list, fw);
free(fw->filename);
free(fw);
mem_free(fw->filename);
mem_free(fw);
if(dw->filewatch_list == NULL) {
dirwatch_delete(dw);

View file

@ -79,8 +79,8 @@ cleanup:
log_info("Loaded %i mappings from '%s'", num_loaded, repr);
}
free(repr);
free(errstr);
mem_free(repr);
mem_free(errstr);
return num_loaded;
}
@ -292,8 +292,8 @@ void gamepad_init(void) {
gamepad.initialized = true;
gamepad.update_needed = true;
gamepad.axes = calloc(GAMEPAD_AXIS_MAX, sizeof(GamepadAxisState));
gamepad.buttons = calloc(GAMEPAD_BUTTON_MAX + GAMEPAD_EMULATED_BUTTON_MAX, sizeof(GamepadButtonState));
gamepad.axes = ALLOC_ARRAY(GAMEPAD_AXIS_MAX, typeof(*gamepad.axes));
gamepad.buttons = ALLOC_ARRAY(GAMEPAD_BUTTON_MAX + GAMEPAD_EMULATED_BUTTON_MAX, typeof(*gamepad.buttons));
gamepad.active_dev_num = GAMEPAD_DEVNUM_INVALID;
gamepad_load_all_mappings();
@ -312,8 +312,8 @@ void gamepad_shutdown(void) {
log_info("Disabled the gamepad subsystem");
free(gamepad.axes);
free(gamepad.buttons);
mem_free(gamepad.axes);
mem_free(gamepad.buttons);
dynarray_foreach_elem(&gamepad.devices, auto dev, {
if(dev->controller) {

View file

@ -386,14 +386,14 @@ HT_DECLARE_FUNC(void, destroy, (HT_BASETYPE *ht))
* ht_XXX_t* ht_XXX_new(void);
*
* Convenience function; allocates and initializes a new hashtable structure.
* You must free() it manually when you're done with it (but don't forget to
* You must mem_free() it manually when you're done with it (but don't forget to
* ht_*_destroy() it as well).
*
* Returns the allocated hashtable structure.
*/
INLINE attr_returns_allocated
HT_DECLARE_FUNC(HT_BASETYPE*, new, (void)) {
HT_BASETYPE *ht = calloc(1, sizeof(HT_BASETYPE));
auto ht = ALLOC(HT_BASETYPE);
HT_FUNC(create)(ht);
return ht;
}
@ -804,7 +804,7 @@ HT_DECLARE_FUNC(void, write_unlock, (HT_BASETYPE *ht)) {
HT_DECLARE_FUNC(void, create, (HT_BASETYPE *ht)) {
ht_size_t size = HT_MIN_SIZE;
ht->elements = calloc(size, sizeof(*ht->elements));
ht->elements = ALLOC_ARRAY(size, typeof(*ht->elements));
ht->num_elements_allocated = size;
ht->num_elements_occupied = 0;
ht->hash_mask = size - 1;
@ -823,7 +823,7 @@ HT_DECLARE_FUNC(void, destroy, (HT_BASETYPE *ht)) {
SDL_DestroyCond(ht->sync.cond);
SDL_DestroyMutex(ht->sync.mutex);
#endif
free(ht->elements);
mem_free(ht->elements);
}
HT_DECLARE_PRIV_FUNC(HT_TYPE(element)*, find_element, (HT_BASETYPE *ht, HT_TYPE(const_key) key, hash_t hash)) {
@ -1150,7 +1150,7 @@ HT_DECLARE_PRIV_FUNC(void, resize, (HT_BASETYPE *ht, size_t new_size)) {
ht_size_t old_size = ht->num_elements_allocated;
HT_PRIV_FUNC(check_elem_count)(ht);
HT_TYPE(element) *new_elements = calloc(new_size, sizeof(*ht->elements));
auto new_elements = ALLOC_ARRAY(new_size, typeof(*ht->elements));
ht->max_psl = 0;
for(ht_size_t i = 0; i < old_size; ++i) {
@ -1164,7 +1164,7 @@ HT_DECLARE_PRIV_FUNC(void, resize, (HT_BASETYPE *ht, size_t new_size)) {
ht->num_elements_allocated = new_size;
ht->hash_mask = new_size - 1;
free(old_elements);
mem_free(old_elements);
/*
log_debug(

View file

@ -18,7 +18,7 @@
#define HT_SUFFIX str2ptr
#define HT_KEY_TYPE char*
#define HT_VALUE_TYPE void*
#define HT_FUNC_FREE_KEY(key) free(key)
#define HT_FUNC_FREE_KEY(key) mem_free(key)
#define HT_FUNC_KEYS_EQUAL(key1, key2) (!strcmp(key1, key2))
#define HT_FUNC_HASH_KEY(key) htutil_hashfunc_string(key)
#define HT_FUNC_COPY_KEY(dst, src) (*(dst) = strdup(src))
@ -38,7 +38,7 @@
#define HT_SUFFIX str2ptr_ts
#define HT_KEY_TYPE char*
#define HT_VALUE_TYPE void*
#define HT_FUNC_FREE_KEY(key) free(key)
#define HT_FUNC_FREE_KEY(key) mem_free(key)
#define HT_FUNC_KEYS_EQUAL(key1, key2) (!strcmp(key1, key2))
#define HT_FUNC_HASH_KEY(key) htutil_hashfunc_string(key)
#define HT_FUNC_COPY_KEY(dst, src) (*(dst) = strdup(src))
@ -59,7 +59,7 @@
#define HT_SUFFIX str2int
#define HT_KEY_TYPE char*
#define HT_VALUE_TYPE int64_t
#define HT_FUNC_FREE_KEY(key) free(key)
#define HT_FUNC_FREE_KEY(key) mem_free(key)
#define HT_FUNC_KEYS_EQUAL(key1, key2) (!strcmp(key1, key2))
#define HT_FUNC_HASH_KEY(key) htutil_hashfunc_string(key)
#define HT_FUNC_COPY_KEY(dst, src) (*(dst) = strdup(src))
@ -78,7 +78,7 @@
#define HT_SUFFIX str2int_ts
#define HT_KEY_TYPE char*
#define HT_VALUE_TYPE int64_t
#define HT_FUNC_FREE_KEY(key) free(key)
#define HT_FUNC_FREE_KEY(key) mem_free(key)
#define HT_FUNC_KEYS_EQUAL(key1, key2) (!strcmp(key1, key2))
#define HT_FUNC_HASH_KEY(key) htutil_hashfunc_string(key)
#define HT_FUNC_COPY_KEY(dst, src) (*(dst) = strdup(src))

View file

@ -368,13 +368,13 @@ void* alist_foreach(ListAnchor *list, ListAnchorForeachCallback callback, void *
void* list_callback_free_element(List **dest, List *elem, void *arg) {
list_unlink(dest, elem);
free(elem);
mem_free(elem);
return NULL;
}
void* alist_callback_free_element(ListAnchor *list, List *elem, void *arg) {
alist_unlink(list, elem);
free(elem);
mem_free(elem);
return NULL;
}
@ -389,7 +389,5 @@ void alist_free_all(ListAnchor *list) {
}
ListContainer* list_wrap_container(void *data) {
ListContainer *c = calloc(1, sizeof(ListContainer));
c->data = data;
return c;
return ALLOC(ListContainer, { .data = data });
}

View file

@ -113,7 +113,7 @@ noreturn static void log_abort(const char *msg) {
if(logging.err_appendix) {
char *m = strfmt("%s\n\n%s", msg, logging.err_appendix);
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, m, NULL);
free(m);
mem_free(m);
} else {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, msg, NULL);
}
@ -258,7 +258,7 @@ static void log_dispatch_async(LogEntry *entry) {
for(Logger *l = logging.outputs; l; l = l->next) {
if(l->levels & entry->level) {
size_t msg_len = strlen(entry->message);
QueuedLogEntry *qle = calloc(1, sizeof(*qle) + msg_len + 1);
auto qle = ALLOC_FLEX(QueuedLogEntry, msg_len + 1);
memcpy(&qle->e, entry, sizeof(*entry));
memcpy(qle->message, entry->message, msg_len);
qle->e.message = qle->message;
@ -356,7 +356,7 @@ static void *delete_logger(List **loggers, List *logger, void *arg) {
SDL_RWsync(l->out);
SDL_RWclose(l->out);
free(list_unlink(loggers, logger));
mem_free(list_unlink(loggers, logger));
return NULL;
}
@ -374,7 +374,7 @@ static int log_queue_thread(void *a) {
while((qle = alist_pop(&logging.queue.queue)) && logging.queue.shutdown < 2) {
SDL_UnlockMutex(mtx);
log_dispatch(&qle->e);
free(qle);
mem_free(qle);
SDL_LockMutex(mtx);
}
@ -446,7 +446,7 @@ void log_shutdown(void) {
dynarray_free_data(&logging.filters);
#ifdef LOG_FATAL_MSGBOX
free(logging.err_appendix);
mem_free(logging.err_appendix);
#endif
memset(&logging, 0, sizeof(logging));
@ -474,10 +474,7 @@ void log_add_output(LogLevel levels, SDL_RWops *output, Formatter *formatter) {
return;
}
Logger *l = calloc(1, sizeof(Logger));
l->levels = levels;
l->out = output;
auto l = ALLOC(Logger, { .levels = levels, .out = output });
formatter(&l->formatter, l->out);
assert(l->formatter.format != NULL);
@ -664,8 +661,8 @@ error:
void log_remove_filters(void) {
dynarray_foreach_elem(&logging.filters, LogFilterEntry *f, {
free(f->patterns.module);
free(f->patterns.func);
mem_free(f->patterns.module);
mem_free(f->patterns.func);
});
logging.filters.num_elements = 0;

View file

@ -87,9 +87,9 @@ static void init_log_file(void) {
"Please report the problem to the developers at https://taisei-project.org/ if it persists.",
logpath
);
free(logpath);
mem_free(logpath);
log_set_gui_error_appendix(m);
free(m);
mem_free(m);
} else {
log_set_gui_error_appendix("Please report the problem to the developers at https://taisei-project.org/ if it persists.");
}
@ -151,7 +151,7 @@ static SDLCALL void sdl_log(void *userdata, int category, SDL_LogPriority priori
}
static void init_sdl(void) {
SDL_version v;
mem_install_sdl_callbacks();
if(SDL_Init(SDL_INIT_EVENTS) < 0) {
log_fatal("SDL_Init() failed: %s", SDL_GetError());
@ -172,6 +172,7 @@ static void init_sdl(void) {
log_info("SDL initialized");
SDL_version v;
SDL_VERSION(&v);
log_info("Compiled against SDL %u.%u.%u", v.major, v.minor, v.patch);
@ -231,13 +232,13 @@ static noreturn void main_vfstree(CallChainResult ccr);
static void cleanup_replay(Replay **rpy) {
if(*rpy) {
replay_reset(*rpy);
free(*rpy);
mem_free(*rpy);
*rpy = NULL;
}
}
static Replay *alloc_replay(void) {
return calloc(1, sizeof(Replay));
return ALLOC(Replay);
}
static noreturn void main_quit(MainContext *ctx, int status) {
@ -262,7 +263,7 @@ static noreturn void main_quit(MainContext *ctx, int status) {
cleanup_replay(&ctx->replay_out);
free(ctx);
mem_free(ctx);
exit(status);
}
@ -279,7 +280,7 @@ int main(int argc, char **argv);
attr_used
int main(int argc, char **argv) {
MainContext *ctx = calloc(1, sizeof(*ctx));
auto ctx = ALLOC(MainContext);
setlocale(LC_ALL, "C");
init_log();
@ -474,7 +475,7 @@ static void main_singlestg_end_game(CallChainResult ccr) {
static void main_singlestg_cleanup(CallChainResult ccr) {
SingleStageContext *ctx = ccr.ctx;
MainContext *mctx = ctx->mctx;
free(ccr.ctx);
mem_free(ccr.ctx);
main_quit(mctx, 0);
}
@ -485,10 +486,11 @@ static void main_singlestg(MainContext *mctx) {
StageInfo *stg = stageinfo_get_by_id(a->stageid);
assert(stg); // properly checked before this
SingleStageContext *ctx = calloc(1, sizeof(*ctx));
ctx->mctx = mctx;
ctx->plrmode = a->plrmode;
ctx->stg = stg;
auto ctx = ALLOC(SingleStageContext, {
.mctx = mctx,
.plrmode = a->plrmode,
.stg = stg,
});
global.diff = stg->difficulty;
global.is_practice_mode = (stg->type != STAGE_EXTRA);

150
src/memory.c Normal file
View file

@ -0,0 +1,150 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "memory.h"
#include <stdlib.h>
#define MEMALIGN_METHOD_C11 0
#define MEMALIGN_METHOD_POSIX 1
#define MEMALIGN_METHOD_WIN32 2
#if defined(TAISEI_BUILDCONF_HAVE_POSIX_MEMALIGN)
#define MEMALIGN_METHOD MEMALIGN_METHOD_POSIX
#elif defined(TAISEI_BUILDCONF_HAVE_ALIGNED_ALLOC)
#define MEMALIGN_METHOD MEMALIGN_METHOD_C11
#elif defined(TAISEI_BUILDCONF_HAVE_ALIGNED_MALLOC_FREE)
#define MEMALIGN_METHOD MEMALIGN_METHOD_WIN32
#else
#error No usable aligned malloc implementation
#endif
void mem_free(void *ptr) {
#if MEMALIGN_METHOD == MEMALIGN_METHOD_WIN32
_aligned_free(ptr);
#else
libc_free(ptr);
#endif
}
static size_t mem_calc_array_size(size_t num_members, size_t size) {
size_t array_size;
if(__builtin_mul_overflow(num_members, size, &array_size)) {
assert(0 && "size_t overflow");
abort();
}
return array_size;
}
void *mem_alloc(size_t size) {
#if MEMALIGN_METHOD == MEMALIGN_METHOD_WIN32
void *p = NOT_NULL(_aligned_malloc(size, alignof(max_align_t)));
memset(p, 0, size);
return p;
#else
return NOT_NULL(libc_calloc(1, size));
#endif
}
void *mem_alloc_array(size_t num_members, size_t size) {
#if MEMALIGN_METHOD == MEMALIGN_METHOD_WIN32
size_t array_size = mem_calc_array_size(num_members, size);
void *p = NOT_NULL(_aligned_malloc(array_size, alignof(max_align_t)));
memset(p, 0, array_size);
return p;
#else
return NOT_NULL(libc_calloc(num_members, size));
#endif
}
void *mem_realloc(void *ptr, size_t size) {
if(ptr == NULL) {
return mem_alloc(size);
}
if(size == 0) {
libc_free(ptr);
return ptr;
}
#if MEMALIGN_METHOD == MEMALIGN_METHOD_WIN32
return NOT_NULL(_aligned_realloc(ptr, size, alignof(max_align_t)));
#else
return NOT_NULL(libc_realloc(ptr, size));
#endif
}
void *mem_alloc_aligned(size_t size, size_t alignment) {
assert((alignment & (alignment - 1)) == 0);
assert((alignment / sizeof(void*)) * sizeof(void*) == alignment);
#if MEMALIGN_METHOD == MEMALIGN_METHOD_C11
size_t nsize = ((size - 1) / alignment + 1) * alignment;
assert(nsize >= size);
void *p = NOT_NULL(aligned_alloc(alignment, nsize));
memset(p, 0, size);
return p;
#elif MEMALIGN_METHOD == MEMALIGN_METHOD_POSIX
void *p;
attr_unused int r = posix_memalign(&p, alignment, size);
assert(r == 0);
assume(p != NULL);
memset(p, 0, size);
return p;
#elif MEMALIGN_METHOD == MEMALIGN_METHOD_WIN32
void *p = NOT_NULL(_aligned_malloc(size, alignment));
memset(p, 0, size);
return p;
#else
#error No usable aligned malloc implementation
#endif
}
void *mem_alloc_array_aligned(size_t num_members, size_t size, size_t alignment) {
return mem_alloc_aligned(mem_calc_array_size(num_members, size), alignment);
}
void *mem_dup(const void *src, size_t size) {
return memcpy(mem_alloc(size), src, size);
}
// SDL's calling convention may differ from the default, so wrap when necessary.
static void SDLCALL mem_sdlcall_free(void *ptr) {
mem_free(ptr);
}
static void *SDLCALL mem_sdlcall_malloc(size_t size) {
return mem_alloc(size);
}
static void *SDLCALL mem_sdlcall_calloc(size_t num_members, size_t size) {
return mem_alloc_array(num_members, size);
}
static void *SDLCALL mem_sdlcall_realloc(void *ptr, size_t size) {
return mem_realloc(ptr, size);
}
#define _mem_choose_compatible_func(orig, wrapper) \
__builtin_choose_expr( \
__builtin_types_compatible_p(typeof(orig), typeof(wrapper)), \
orig, wrapper)
void mem_install_sdl_callbacks(void) {
SDL_SetMemoryFunctions(
_mem_choose_compatible_func(mem_alloc, mem_sdlcall_malloc),
_mem_choose_compatible_func(mem_alloc_array, mem_sdlcall_calloc),
_mem_choose_compatible_func(mem_realloc, mem_sdlcall_realloc),
_mem_choose_compatible_func(mem_free, mem_sdlcall_free)
);
}

122
src/memory.h Normal file
View file

@ -0,0 +1,122 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#pragma once
#include "taisei.h"
#include "util/macrohax.h"
#include <SDL.h>
/*
* NOTE: All allocation functions return zero-initialized memory (except realloc) and never NULL.
*/
void mem_free(void *ptr);
void *mem_alloc(size_t size)
attr_malloc
attr_dealloc(mem_free, 1)
attr_alloc_size(1)
attr_returns_allocated;
void *mem_alloc_array(size_t num_members, size_t size)
attr_malloc
attr_dealloc(mem_free, 1)
attr_alloc_size(1, 2)
attr_returns_allocated;
void *mem_realloc(void *ptr, size_t size)
attr_dealloc(mem_free, 1)
attr_alloc_size(2)
attr_returns_allocated;
void *mem_alloc_aligned(size_t size, size_t alignment)
attr_malloc
attr_dealloc(mem_free, 1)
attr_alloc_size(1)
attr_alloc_align(2)
attr_returns_allocated;
void *mem_alloc_array_aligned(size_t num_members, size_t size, size_t alignment)
attr_malloc
attr_dealloc(mem_free, 1)
attr_alloc_size(1, 2)
attr_alloc_align(3)
attr_returns_allocated;
void *mem_dup(const void *src, size_t size)
attr_malloc
attr_dealloc(mem_free, 1)
attr_alloc_size(2)
attr_returns_allocated;
#define memdup mem_dup
/*
* This macro transparently handles allocation of both normal and over-aligned types.
* The allocation must be free'd with mem_free().
*
* You should use it like this:
*
* auto x = ALLOC(MyType); // x is a pointer to MyType
*
* This style is also allowed:
*
* MyType *x = ALLOC(typeof(*x));
*
* This style can also be used, but is not recommended:
*
* MyType *x = ALLOC(MyType);
*
* You can also provide an optional initializer as the second argument, like so:
*
* auto x = ALLOC(MyStructType, { .foo = 42, .bar = 69 });
*/
#define ALLOC(_type, ...)\
MACROHAX_OVERLOAD_HASARGS(ALLOC_, __VA_ARGS__)(_type, ##__VA_ARGS__)
#define ALLOC_0(_type) \
(_type *)__builtin_choose_expr( \
alignof(_type) > alignof(max_align_t), \
mem_alloc_aligned(sizeof(_type), alignof(_type)), \
mem_alloc(sizeof(_type)))
#define ALLOC_1(_type, ...) ({ \
auto _alloc_ptr = ALLOC_0(_type); \
*_alloc_ptr = (_type) __VA_ARGS__; \
_alloc_ptr; \
})
/*
* Like ALLOC(), but for array allocations:
*
* auto x = ALLOC_ARRAY(10, int); // x is an int*
*
* Unlike ALLOC(), this macro does not accept an optional initializer.
*/
#define ALLOC_ARRAY(_nmemb, _type) \
(_type *)__builtin_choose_expr( \
alignof(_type) > alignof(max_align_t), \
mem_alloc_array_aligned(_nmemb, sizeof(_type), alignof(_type)), \
mem_alloc_array(_nmemb, sizeof(_type)))
/*
* Like ALLOC(), but for structs with flexible array members:
*
* auto x = ALLOC(MyFlexStruct, 42); // allocates sizeof(MyFlexStruct)+42 bytes
*
* Unlike ALLOC(), this macro does not accept an optional initializer.
*/
#define ALLOC_FLEX(_type, _extra_size) \
(_type *)__builtin_choose_expr( \
alignof(_type) > alignof(max_align_t), \
mem_alloc_aligned(sizeof(_type) + (_extra_size), alignof(_type)), \
mem_alloc(sizeof(_type) + (_extra_size)))
void mem_install_sdl_callbacks(void);

View file

@ -320,7 +320,7 @@ static void add_character(MenuData *m, int i) {
}
static void charprofile_free(MenuData *m) {
free(m->context);
mem_free(m->context);
}
// TODO: add a better drawing animation for character selection
@ -390,9 +390,7 @@ MenuData *create_charprofile_menu(void) {
m->end = charprofile_free;
m->transition = TransFadeBlack;
m->flags = MF_Abortable;
CharProfileContext *ctx = calloc(1, sizeof(*ctx));
m->context = ctx;
m->context = ALLOC(CharProfileContext);