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:
parent
8a3a82c7ed
commit
b6978178b1
117 changed files with 1066 additions and 756 deletions
|
@ -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'
|
||||
|
|
|
@ -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--;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
19
src/config.c
19
src/config.c
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
|
|
19
src/log.c
19
src/log.c
|
@ -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;
|
||||
|
|
26
src/main.c
26
src/main.c
|
@ -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
150
src/memory.c
Normal 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
122
src/memory.h
Normal 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);
|
|
@ -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);
|
||||
|
||||