Minor clean up of permanent/temporary arena usage

This commit is contained in:
gingerBill
2025-09-10 18:20:20 +01:00
parent af37ba76c1
commit 21b1173076
9 changed files with 98 additions and 55 deletions

View File

@@ -1126,7 +1126,7 @@ gb_internal String internal_odin_root_dir(void) {
mutex_lock(&string_buffer_mutex);
defer (mutex_unlock(&string_buffer_mutex));
text = gb_alloc_array(permanent_allocator(), wchar_t, len+1);
text = permanent_alloc_array<wchar_t>(len+1);
GetModuleFileNameW(nullptr, text, cast(int)len);
path = string16_to_string(heap_allocator(), make_string16(cast(u16 *)text, len));
@@ -1181,7 +1181,7 @@ gb_internal String internal_odin_root_dir(void) {
mutex_lock(&string_buffer_mutex);
defer (mutex_unlock(&string_buffer_mutex));
text = gb_alloc_array(permanent_allocator(), u8, len + 1);
text = permanent_alloc_array<u8>(len + 1);
gb_memmove(text, &path_buf[0], len);
path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr);
@@ -1233,7 +1233,7 @@ gb_internal String internal_odin_root_dir(void) {
mutex_lock(&string_buffer_mutex);
defer (mutex_unlock(&string_buffer_mutex));
text = gb_alloc_array(permanent_allocator(), u8, len + 1);
text = permanent_alloc_array<u8>(len + 1);
gb_memmove(text, &path_buf[0], len);
path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr);
@@ -1394,7 +1394,7 @@ gb_internal String internal_odin_root_dir(void) {
mutex_lock(&string_buffer_mutex);
defer (mutex_unlock(&string_buffer_mutex));
text = gb_alloc_array(permanent_allocator(), u8, len + 1);
text = permanent_alloc_array<u8>(len + 1);
gb_memmove(text, &path_buf[0], len);
@@ -1429,7 +1429,7 @@ gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_) {
len = GetFullPathNameW(cast(wchar_t *)&string16[0], 0, nullptr, nullptr);
if (len != 0) {
wchar_t *text = gb_alloc_array(permanent_allocator(), wchar_t, len+1);
wchar_t *text = permanent_alloc_array<wchar_t>(len+1);
GetFullPathNameW(cast(wchar_t *)&string16[0], len, text, nullptr);
mutex_unlock(&fullpath_mutex);

View File

@@ -83,7 +83,7 @@ i32 bundle_android(String original_init_directory) {
return 1;
}
int *dir_numbers = gb_alloc_array(temporary_allocator(), int, possible_valid_dirs.count);
int *dir_numbers = temporary_alloc_array<int>(possible_valid_dirs.count);
char buf[1024] = {};
for_array(i, possible_valid_dirs) {

View File

@@ -354,7 +354,7 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
}
isize const arg_offset = 1;
auto param_types = slice_make<Type *>(permanent_allocator(), ce->args.count-arg_offset);
auto param_types = permanent_slice_make<Type *>(ce->args.count-arg_offset);
param_types[0] = t_objc_id;
param_types[1] = sel_type;
@@ -462,7 +462,7 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
{
// NOTE(harold): The last argument specified in the call is the handler proc,
// any other arguments before it are capture by-copy arguments.
auto param_operands = slice_make<Operand>(permanent_allocator(), ce->args.count);
auto param_operands = permanent_slice_make<Operand>(ce->args.count);
isize capture_arg_count = ce->args.count - 1;
@@ -3554,7 +3554,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
case BuiltinProc_compress_values: {
Operand *ops = gb_alloc_array(temporary_allocator(), Operand, ce->args.count);
Operand *ops = temporary_alloc_array<Operand>(ce->args.count);
isize value_count = 0;
@@ -3685,9 +3685,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
operand->mode = Addressing_Value;
} else {
Type *st = alloc_type_struct_complete();
st->Struct.fields = slice_make<Entity *>(permanent_allocator(), value_count);
st->Struct.tags = gb_alloc_array(permanent_allocator(), String, value_count);
st->Struct.offsets = gb_alloc_array(permanent_allocator(), i64, value_count);
st->Struct.fields = permanent_slice_make<Entity *>(value_count);
st->Struct.tags = permanent_alloc_array<String>(value_count);
st->Struct.offsets = permanent_alloc_array<i64>(value_count);
Scope *scope = create_scope(c->info, nullptr);
@@ -4387,7 +4387,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
elem = alloc_type_struct();
elem->Struct.scope = s;
elem->Struct.fields = slice_from_array(fields);
elem->Struct.tags = gb_alloc_array(permanent_allocator(), String, fields.count);
elem->Struct.tags = permanent_alloc_array<String>(fields.count);
elem->Struct.node = dummy_node_struct;
type_set_offsets(elem);
wait_signal_set(&elem->Struct.fields_wait_signal);
@@ -4419,7 +4419,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
gb_string_free(s);
return false;
}
auto types = slice_make<Type *>(permanent_allocator(), t->Struct.fields.count-1);
auto types = permanent_slice_make<Type *>(t->Struct.fields.count-1);
for_array(i, types) {
Entity *f = t->Struct.fields[i];
GB_ASSERT(f->type->kind == Type_MultiPointer);
@@ -4755,8 +4755,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
if (is_type_array(elem)) {
Type *old_array = base_type(elem);
soa_struct = alloc_type_struct();
soa_struct->Struct.fields = slice_make<Entity *>(heap_allocator(), cast(isize)old_array->Array.count);
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, cast(isize)old_array->Array.count);
soa_struct->Struct.fields = permanent_slice_make<Entity *>(cast(isize)old_array->Array.count);
soa_struct->Struct.tags = permanent_alloc_array<String>(cast(isize)old_array->Array.count);
soa_struct->Struct.node = operand->expr;
soa_struct->Struct.soa_kind = StructSoa_Fixed;
soa_struct->Struct.soa_elem = elem;
@@ -4788,8 +4788,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
Type *old_struct = base_type(elem);
soa_struct = alloc_type_struct();
soa_struct->Struct.fields = slice_make<Entity *>(heap_allocator(), old_struct->Struct.fields.count);
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, old_struct->Struct.fields.count);
soa_struct->Struct.fields = permanent_slice_make<Entity *>(old_struct->Struct.fields.count);
soa_struct->Struct.tags = permanent_alloc_array<String>(old_struct->Struct.fields.count);
soa_struct->Struct.node = operand->expr;
soa_struct->Struct.soa_kind = StructSoa_Fixed;
soa_struct->Struct.soa_elem = elem;
@@ -6181,7 +6181,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
Type *new_type = alloc_type_union();
auto variants = slice_make<Type *>(permanent_allocator(), bt->Union.variants.count);
auto variants = permanent_slice_make<Type *>(bt->Union.variants.count);
for_array(i, bt->Union.variants) {
variants[i] = alloc_type_pointer(bt->Union.variants[i]);
}

View File

@@ -4639,7 +4639,6 @@ gb_internal ExactValue convert_exact_value_for_type(ExactValue v, Type *type) {
}
gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
// GB_ASSERT_NOT_NULL(target_type);
if (target_type == nullptr || operand->mode == Addressing_Invalid ||
operand->mode == Addressing_Type ||
is_type_typed(operand->type) ||
@@ -4810,7 +4809,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
TEMPORARY_ALLOCATOR_GUARD();
isize count = t->Union.variants.count;
ValidIndexAndScore *valids = gb_alloc_array(temporary_allocator(), ValidIndexAndScore, count);
ValidIndexAndScore *valids = temporary_alloc_array<ValidIndexAndScore>(count);
isize valid_count = 0;
isize first_success_index = -1;
for_array(i, t->Union.variants) {
@@ -6257,7 +6256,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
}
GB_ASSERT(ce->split_args);
auto visited = slice_make<bool>(temporary_allocator(), pt->param_count);
auto visited = temporary_slice_make<bool>(pt->param_count);
auto ordered_operands = array_make<Operand>(temporary_allocator(), pt->param_count);
defer ({
for (Operand const &o : ordered_operands) {
@@ -7254,7 +7253,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
}
// Try to reduce the list further for `$T: typeid` like parameters
bool *possibly_ignore = gb_alloc_array(temporary_allocator(), bool, procs.count);
bool *possibly_ignore = temporary_alloc_array<bool>(procs.count);
isize possibly_ignore_set = 0;
if (true) {
@@ -7342,7 +7341,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
}
isize max_spaces = gb_max(max_name_length, max_type_length);
char *spaces = gb_alloc_array(temporary_allocator(), char, max_spaces+1);
char *spaces = temporary_alloc_array<char>(max_spaces+1);
for (isize i = 0; i < max_spaces; i++) {
spaces[i] = ' ';
}
@@ -7706,7 +7705,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
} else {
TEMPORARY_ALLOCATOR_GUARD();
bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count);
bool *visited = temporary_alloc_array<bool>(param_count);
// LEAK(bill)
ordered_operands = array_make<Operand>(permanent_allocator(), param_count);
@@ -8881,7 +8880,7 @@ gb_internal void add_constant_switch_case(CheckerContext *ctx, SeenMap *seen, Op
isize count = multi_map_count(seen, key);
if (count) {
TEMPORARY_ALLOCATOR_GUARD();
TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
TypeAndToken *taps = temporary_alloc_array<TypeAndToken>(count);
multi_map_get_all(seen, key, taps);
for (isize i = 0; i < count; i++) {
@@ -10927,7 +10926,7 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast
}
}
auto modified_args = slice_make<Ast *>(heap_allocator(), ce->args.count+1);
auto modified_args = permanent_slice_make<Ast *>(ce->args.count+1);
modified_args[0] = first_arg;
slice_copy(&modified_args, ce->args, 1);
ce->args = modified_args;

View File

@@ -1963,7 +1963,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
}
auto rhs = slice_from_array(vals);
auto lhs = slice_make<Ast *>(temporary_allocator(), rhs.count);
auto lhs = temporary_slice_make<Ast *>(rhs.count);
slice_copy(&lhs, rs->vals);
isize addressable_index = cast(isize)is_map;

View File

@@ -1106,7 +1106,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type,
GB_ASSERT(fields.count <= bf->fields.count);
auto bit_offsets = slice_make<i64>(permanent_allocator(), fields.count);
auto bit_offsets = permanent_slice_make<i64>(fields.count);
i64 curr_offset = 0;
for_array(i, bit_sizes) {
bit_offsets[i] = curr_offset;
@@ -2707,7 +2707,7 @@ gb_internal Type *get_map_cell_type(Type *type) {
// Padding exists
Type *s = alloc_type_struct();
Scope *scope = create_scope(nullptr, nullptr);
s->Struct.fields = slice_make<Entity *>(permanent_allocator(), 2);
s->Struct.fields = permanent_slice_make<Entity *>(2);
s->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("v"), alloc_type_array(type, len), false, 0, EntityState_Resolved);
s->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("_"), alloc_type_array(t_u8, padding), false, 1, EntityState_Resolved);
s->Struct.scope = scope;
@@ -2732,7 +2732,7 @@ gb_internal void init_map_internal_debug_types(Type *type) {
Type *metadata_type = alloc_type_struct();
Scope *metadata_scope = create_scope(nullptr, nullptr);
metadata_type->Struct.fields = slice_make<Entity *>(permanent_allocator(), 5);
metadata_type->Struct.fields = permanent_slice_make<Entity *>(5);
metadata_type->Struct.fields[0] = alloc_entity_field(metadata_scope, make_token_ident("key"), key, false, 0, EntityState_Resolved);
metadata_type->Struct.fields[1] = alloc_entity_field(metadata_scope, make_token_ident("value"), value, false, 1, EntityState_Resolved);
metadata_type->Struct.fields[2] = alloc_entity_field(metadata_scope, make_token_ident("hash"), t_uintptr, false, 2, EntityState_Resolved);
@@ -2750,7 +2750,7 @@ gb_internal void init_map_internal_debug_types(Type *type) {
Scope *scope = create_scope(nullptr, nullptr);
Type *debug_type = alloc_type_struct();
debug_type->Struct.fields = slice_make<Entity *>(permanent_allocator(), 3);
debug_type->Struct.fields = permanent_slice_make<Entity *>(3);
debug_type->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("data"), metadata_type, false, 0, EntityState_Resolved);
debug_type->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("len"), t_int, false, 1, EntityState_Resolved);
debug_type->Struct.fields[2] = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, 2, EntityState_Resolved);
@@ -2983,13 +2983,13 @@ gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finis
if (wait_to_finish) {
wait_signal_until_available(&old_struct->Struct.fields_wait_signal);
} else {
GB_ASSERT(old_struct->Struct.fields_wait_signal.futex.load());
GB_ASSERT(old_struct->Struct.fields_wait_signal.futex.load() != 0);
}
field_count = old_struct->Struct.fields.count;
t->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
t->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
t->Struct.fields = permanent_slice_make<Entity *>(field_count+extra_field_count);
t->Struct.tags = permanent_alloc_array<String>(field_count+extra_field_count);
auto const &add_entity = [](Scope *scope, Entity *entity) {
@@ -3107,7 +3107,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
if (is_polymorphic) {
field_count = 0;
soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
soa_struct->Struct.fields = permanent_slice_make<Entity *>(field_count+extra_field_count);
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
soa_struct->Struct.soa_count = 0;
@@ -3117,7 +3117,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
Type *old_array = base_type(elem);
field_count = cast(isize)old_array->Array.count;
soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
soa_struct->Struct.fields = permanent_slice_make<Entity *>(field_count+extra_field_count);
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
string_map_init(&scope->elements, 8);
@@ -3159,8 +3159,8 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
if (old_struct->Struct.fields_wait_signal.futex.load()) {
field_count = old_struct->Struct.fields.count;
soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
soa_struct->Struct.fields = permanent_slice_make<Entity *>(field_count+extra_field_count);
soa_struct->Struct.tags = permanent_alloc_array<String>(field_count+extra_field_count);
for_array(i, old_struct->Struct.fields) {
Entity *old_field = old_struct->Struct.fields[i];
@@ -3355,7 +3355,7 @@ gb_internal void check_array_type_internal(CheckerContext *ctx, Ast *e, Type **t
}
}
gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) {
GB_ASSERT_NOT_NULL(type);
GB_ASSERT(type != nullptr);
if (e == nullptr) {
*type = t_invalid;
return true;

View File

@@ -6177,7 +6177,7 @@ gb_internal bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *u
switch (pi->decl->proc_checked_state.load()) {
case ProcCheckedState_InProgress:
if (e) {
GB_ASSERT(global_procedure_body_in_worker_queue.load());
GB_ASSERT(global_procedure_body_in_worker_queue.load() != 0);
}
return false;
case ProcCheckedState_Checked:
@@ -6431,7 +6431,7 @@ gb_internal WORKER_TASK_PROC(check_proc_info_worker_proc) {
gb_internal void check_init_worker_data(Checker *c) {
u32 thread_count = cast(u32)global_thread_pool.threads.count;
check_procedure_bodies_worker_data = gb_alloc_array(permanent_allocator(), CheckProcedureBodyWorkerData, thread_count);
check_procedure_bodies_worker_data = permanent_alloc_array<CheckProcedureBodyWorkerData>(thread_count);
for (isize i = 0; i < thread_count; i++) {
check_procedure_bodies_worker_data[i].c = c;
@@ -6493,7 +6493,7 @@ gb_internal Type *tuple_to_pointers(Type *ot) {
Type *t = alloc_type_tuple();
t->Tuple.variables = slice_make<Entity *>(heap_allocator(), ot->Tuple.variables.count);
t->Tuple.variables = permanent_slice_make<Entity *>(ot->Tuple.variables.count);
Scope *scope = nullptr;
for_array(i, t->Tuple.variables) {

View File

@@ -353,7 +353,7 @@ gb_internal gbAllocator arena_allocator(Arena *arena) {
gb_internal GB_ALLOCATOR_PROC(arena_allocator_proc) {
void *ptr = nullptr;
Arena *arena = cast(Arena *)allocator_data;
GB_ASSERT_NOT_NULL(arena);
GB_ASSERT(arena != nullptr);
switch (type) {
case gbAllocation_Alloc:
@@ -401,6 +401,48 @@ gb_internal Arena *get_arena(ThreadArenaKind kind) {
}
template <typename T>
gb_internal T *permanent_alloc_item() {
Arena *arena = get_arena(ThreadArena_Permanent);
return arena_alloc_item<T>(arena);
}
template <typename T>
gb_internal T *permanent_alloc_array(isize count) {
Arena *arena = get_arena(ThreadArena_Permanent);
return cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T));
}
template <typename T>
gb_internal Slice<T> permanent_slice_make(isize count) {
Arena *arena = get_arena(ThreadArena_Permanent);
T *data = cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T));
return {data, count};
}
template <typename T>
gb_internal T *temporary_alloc_item() {
Arena *arena = get_arena(ThreadArena_Temporary);
return arena_alloc_item<T>(arena);
}
template <typename T>
gb_internal T *temporary_alloc_array(isize count) {
Arena *arena = get_arena(ThreadArena_Temporary);
return cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T));
}
template <typename T>
gb_internal Slice<T> temporary_slice_make(isize count) {
Arena *arena = get_arena(ThreadArena_Temporary);
T *data = cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T));
return {data, count};
}
gb_internal GB_ALLOCATOR_PROC(thread_arena_allocator_proc) {
void *ptr = nullptr;

View File

@@ -714,13 +714,15 @@ extern "C++" {
} while (0)
#endif
#if defined(DISABLE_ASSERT)
#define GB_ASSERT(cond) gb_unused(cond)
#endif
#ifndef GB_ASSERT
#define GB_ASSERT(cond) GB_ASSERT_MSG(cond, NULL)
#endif
#ifndef GB_ASSERT_NOT_NULL
#define GB_ASSERT_NOT_NULL(ptr) GB_ASSERT_MSG((ptr) != NULL, #ptr " must not be NULL")
#endif
// NOTE(bill): Things that shouldn't happen with a message!
#ifndef GB_PANIC
@@ -3719,7 +3721,7 @@ gb_inline i32 gb_strcmp(char const *s1, char const *s2) {
}
gb_inline char *gb_strcpy(char *dest, char const *source) {
GB_ASSERT_NOT_NULL(dest);
GB_ASSERT(dest != NULL);
if (source) {
char *str = dest;
while (*source) *str++ = *source++;
@@ -3729,7 +3731,7 @@ gb_inline char *gb_strcpy(char *dest, char const *source) {
gb_inline char *gb_strncpy(char *dest, char const *source, isize len) {
GB_ASSERT_NOT_NULL(dest);
GB_ASSERT(dest != NULL);
if (source) {
char *str = dest;
while (len > 0 && *source) {
@@ -3746,7 +3748,7 @@ gb_inline char *gb_strncpy(char *dest, char const *source, isize len) {
gb_inline isize gb_strlcpy(char *dest, char const *source, isize len) {
isize result = 0;
GB_ASSERT_NOT_NULL(dest);
GB_ASSERT(dest != NULL);
if (source) {
char const *source_start = source;
char *str = dest;
@@ -5636,7 +5638,7 @@ gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, char con
void gb_file_free_contents(gbFileContents *fc) {
if (fc == NULL || fc->size == 0) return;
GB_ASSERT_NOT_NULL(fc->data);
GB_ASSERT(fc->data != NULL);
gb_free(fc->allocator, fc->data);
fc->data = NULL;
fc->size = 0;
@@ -5648,7 +5650,7 @@ void gb_file_free_contents(gbFileContents *fc) {
gb_inline b32 gb_path_is_absolute(char const *path) {
b32 result = false;
GB_ASSERT_NOT_NULL(path);
GB_ASSERT(path != NULL);
#if defined(GB_SYSTEM_WINDOWS)
result == (gb_strlen(path) > 2) &&
gb_char_is_alpha(path[0]) &&
@@ -5663,7 +5665,7 @@ gb_inline b32 gb_path_is_relative(char const *path) { return !gb_path_is_absolut
gb_inline b32 gb_path_is_root(char const *path) {
b32 result = false;
GB_ASSERT_NOT_NULL(path);
GB_ASSERT(path != NULL);
#if defined(GB_SYSTEM_WINDOWS)
result = gb_path_is_absolute(path) && (gb_strlen(path) == 3);
#else
@@ -5674,14 +5676,14 @@ gb_inline b32 gb_path_is_root(char const *path) {
gb_inline char const *gb_path_base_name(char const *path) {
char const *ls;
GB_ASSERT_NOT_NULL(path);
GB_ASSERT(path != NULL);
ls = gb_char_last_occurence(path, '/');
return (ls == NULL) ? path : ls+1;
}
gb_inline char const *gb_path_extension(char const *path) {
char const *ld;
GB_ASSERT_NOT_NULL(path);
GB_ASSERT(path != NULL);
ld = gb_char_last_occurence(path, '.');
return (ld == NULL) ? NULL : ld+1;
}