165 lines
6.5 KiB
C
Executable File
165 lines
6.5 KiB
C
Executable File
/// __ ___ __ ___ _ __ __ _
|
|
/// \ \/ / '__/ _ \ '_ \ / _` |
|
|
/// > <| | | __/ | | | (_| |
|
|
/// /_/\_\_| \___|_| |_|\__,_|
|
|
///
|
|
/// Copyright (c) 1997 - Ognjen 'xolatile' Milan Robovic
|
|
///
|
|
/// xolatile@chud.cyou - xrena - Probably the most minimalistic arena allocator possible, and undoubtedly evil in implementation details.
|
|
///
|
|
/// This program is free software, free as in freedom and as in free beer, you can redistribute it and/or modify it under the terms of the GNU
|
|
/// General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version if you wish...
|
|
///
|
|
/// This program is distributed in the hope that it will be useful, but it is probably not, and without any warranty, without even the implied
|
|
/// warranty of merchantability or fitness for a particular purpose, because it is pointless. Please see the GNU (Geenoo) General Public License
|
|
/// for more details, if you dare, it is a lot of text that nobody wants to read...
|
|
|
|
/// Description
|
|
///
|
|
/// Xrena, extremely small header only library serving as arena allocator, with one global variable.In order to use it, include this header file,
|
|
/// there's no macro for including implementation (like stb libraries), this is for projects that have only one C source file, and one or more C
|
|
/// header files. I'll provide minimal examples below.
|
|
|
|
/// Maximum size for single memory "allocation", if you're making a parser, you can use as low as 8 or 16, but as soon as you start working with
|
|
/// bigger structures, images or models, you need this to be higher value, for example 64 * 1024 * 1024 (loading 4k textures). If you don't
|
|
/// understand how to use this macro, turn off your machine and read some books, or alternatively, fuck around until you find out.
|
|
|
|
#ifndef arena_block_limit
|
|
#define arena_block_limit (1024 * 1024)
|
|
#endif
|
|
|
|
/// Structure and global variable definition, you shouldn't ever modify this, functions below cover that task.
|
|
|
|
static struct {
|
|
caliber block_count;
|
|
caliber block_limit;
|
|
struct {
|
|
caliber count;
|
|
caliber capacity;
|
|
character * buffer;
|
|
} * * block_array;
|
|
} * arena_memory = null;
|
|
|
|
/// Warning: Local function! You don't need to use this at all, it's covered by other functions, don't worry about it.
|
|
///
|
|
/// This function will initialize the state of variable 'arena_memory', or extend it's size once memory is filled.
|
|
|
|
static procedure arena_begin_or_widen (none) {
|
|
caliber current = ++arena_memory->block_count - 1;
|
|
|
|
arena_memory->block_limit = arena_block_limit;
|
|
|
|
arena_memory->block_array = reallocate (arena_memory->block_array, arena_memory->block_count * sizeof (* arena_memory->block_array));
|
|
|
|
arena_memory->block_array [current] = allocate (sizeof (* arena));
|
|
|
|
arena_memory->block_array [current]->buffer = allocate (arena_block_limit);
|
|
arena_memory->block_array [current]->count = 0;
|
|
arena_memory->block_array [current]->capacity = arena_block_limit;
|
|
}
|
|
|
|
/// Warning: Local function! You don't need to use this at all, it's covered by other functions, don't worry about it.
|
|
///
|
|
/// This function will clean all memory pools from variable 'arena_memory', freeing all memory used by the arena allocator.
|
|
|
|
static procedure arena_clean (none) {
|
|
for (caliber index = 0; index < arena_memory->block_count; ++index) {
|
|
arena_memory->block_array [index]->buffer = deallocate (arena_memory->block_array [index]->buffer);
|
|
arena_memory->block_array [index] = deallocate (arena_memory->block_array [index]);
|
|
}
|
|
|
|
arena_memory->block_array = deallocate (arena_memory->block_array);
|
|
arena_memory = deallocate (arena_memory);
|
|
}
|
|
|
|
/// Now, this is finally a function that you want to use. You want to allocate object of some size, just add that size in arena, it'll return
|
|
/// you a pointer to that memory location, the same way you'd use 'malloc', and that memory will be zero initialized (unlike 'malloc', just like
|
|
/// 'calloc'). You don't ever have to worry about freeing memory, if your program has definite exit routine. Internally, it checks if arena
|
|
/// allocator was initialized, if it was, it'll extend it if there's not enough memory and return that pointer, otherwise, it will initialize it
|
|
/// and set clean up call at exit.
|
|
///
|
|
/// Make an array of 60 integers, they'll all be zero.
|
|
///
|
|
/// integer * my_integers = arena_add (60 * sizeof (my_integers));
|
|
|
|
static generic * arena_add (caliber size) {
|
|
caliber current = arena_memory->block_count - 1;
|
|
|
|
if (arena == null) {
|
|
clean_up (arena_clean);
|
|
|
|
arena_memory = allocate (sizeof (* arena_memory));
|
|
|
|
arena_begin_or_widen ();
|
|
}
|
|
|
|
fatal_failure (size > arena_memory->block_limit, "arena_add: Block limit reached.");
|
|
|
|
if (arena_memory->block_array [current]->count + size > arena_memory->block_array [current]->capacity) {
|
|
arena_begin_or_widen ();
|
|
}
|
|
|
|
arena_memory->block_array [current]->count += size;
|
|
|
|
return ((generic *) & arena_memory->block_array [current]->buffer [arena_memory->block_array [current]->count - size]);
|
|
}
|
|
|
|
/// Add null terminated string to arena memory.
|
|
///
|
|
/// character * my_token = arena_add_string (parsed_token);
|
|
|
|
static character * arena_add_string (character * string) {
|
|
character * pointer = arena_add (string_length (string) * sizeof (pointer));
|
|
|
|
string_copy (pointer, string);
|
|
|
|
return (pointer);
|
|
}
|
|
|
|
/// Add raw memory of certain size to arena memory.
|
|
///
|
|
/// generic * my_model = arena_add_memory (raw_model_data, size_of_model_data);
|
|
|
|
static generic * arena_add_memory (generic * memory, caliber size) {
|
|
generic * pointer = arena_add (size);
|
|
|
|
memory_copy (pointer, memory, size);
|
|
|
|
return (pointer);
|
|
}
|
|
|
|
/// Add file data from certain path, specify file flags (similar to 'open' system call) and set if you want it to be null terminated.
|
|
///
|
|
/// generic * my_binary_file = arena_add_file ("foo.bin", file_flag_read, false);
|
|
/// character * my_text_file = arena_add_file ("foo.txt", file_flag_read, true);
|
|
|
|
static character * arena_add_file (character * path, integer flag, boolean null_terminate) {
|
|
integer file = -1;
|
|
caliber size = 0;
|
|
character * data = null;
|
|
|
|
file = file_open (path, flag);
|
|
size = file_size (path) + (caliber) null_terminate;
|
|
data = arena_add (size);
|
|
|
|
file_read (file, data, size - (caliber) null_terminate);
|
|
|
|
file = file_close (file);
|
|
|
|
return (data);
|
|
}
|
|
|
|
/// Simple utility function that returns how many bytes have been added to arena allocator.
|
|
///
|
|
/// print ("So far I used %l bytes of memory...\n", arena_usage ());
|
|
|
|
static caliber arena_usage (none) {
|
|
caliber usage = 0;
|
|
|
|
for (caliber block = 0; block < arena_memory->block_count; ++block) {
|
|
usage += arena_memory->block_array [block]->count;
|
|
}
|
|
|
|
return (usage);
|
|
}
|