- C 93.4%
- Makefile 6.6%
| tests | build in /_o | |
| .gitignore | build in /_o | |
| arg.h | fixed EARGF() to not abort (follow doc) | |
| ator.h | memory size type | |
| LICENSE | change pmem size type and add license | |
| pmem.h | memory size type | |
| README.md | doc pmem with simple example | |
My little set of C utilities
License
As most of this stuff is taken/adapted from free or public domain source code from other people, it is published in a dual permissive license: public domain unlicense.org or MIT. See LICENSE file for details.
arg.h
Quick parse command line arguments. Taken from suckless tools, with a few changes:
argcandargvare not hardcoded, but are defaults. Define the macrosARGCandARGVto rename themargv0is not hardcoded and can be declared after the include, with the defaultARGVARvariable name (which should be aconst char **). Define the macroARGVARto rename itARGrepresents the current option argument letter (typechar)EARGF(x)does notabort()but let the called function handling terminaison
Complete example with added features:
#define ARGC arg_count
#define ARGV arg_ptrs
#define ARGVAR program_name
#include "arg.h"
static const char *program_name;
static void
usage(void) {
/* print error and exit */
}
int
main(int arg_count, const char **arg_ptrs) {
{
unsigned char boolean_opt = 0;
const char *display_opt = 0;
const char *file_required = 0;
/* parse command line arguments */
ARGBEGIN {
case 'b': boolean_opt = 1; break;
case 'd': display_opt = ARGF(); break;
case 'f': file_required = EARGF(usage()); break;
} ARGEND;
/* process options */
}
return 0;
}
ator.h
Define an allocator interface that can be customized. It got its origin in the Nuklear project where memory handling is not bound to the standard library.
The struct ator is very simple:
- a
mem()function pointer doing allocation and deallocation (see below) - a
userdatapointer for any custom data needed by a custom allocator
3 macros then do a shortcut over the mem() function with file/line references:
ATOR_ALLOC(ator, size)allocatessizebytes withatorallocatorATOR_REALLOC(ator, ptr, size)reallocatessizebytes withatorallocator onptr, returning the modified pointerATOR_FREE(ator, ptr)frees memory allocated onptrbyatorallocator
The mem() function has the prototype:
void *mem(void *userdata, void *ptr, ator_size size, const char *file, int line);
ator_size is the size_t equivalent without needing to include a standard
library header (see below for changing the implementation).
The behavior is as follow:
- if
ptris null andsizeis0, nothing is done (0is returned) - if
ptris null andsizenot0, allocatessizebytes and returns the pointer - if
ptris not null andsizeis0, frees memory onptr, returns null - if
ptris not null andsizenot0, reallocatessizebytes from previously allocated memory onptrand returns the new pointer
Usage
The following macros can be defined prior inclusion:
| Macro | Description |
|---|---|
ATOR_IMPLEMENTATION |
define in one (and only one) source file, useful only for ATOR_STD_ALLOC |
ATOR_STD_ASSERT |
use assert() from assert.h |
ATOR_STD_TYPES |
use size_t from stddef.h for ator_size type |
ATOR_STD_ALLOC |
define a ator_std_init() function to create an allocator using realloc() and free() |
Some behavior can be overriden by defining some other macros:
| Macro | Description |
|---|---|
ATOR_ASSERT |
use your custom assertion compatible with the standard one |
ATOR_SIZE_TYPE |
use a specific size_t equivalent |
Simple example using std allocator:
#define ATOR_IMPLEMENTATION
#define ATOR_STD_SIZE
#define ATOR_STD_ASSERT
#define ATOR_STD_ALLOC
#include "ator.h"#include <string.h>
int
main(int argc, char **argv) {
struct ator ator = {0};
char *ptr = 0;
// use ator the same than malloc(), realloc(), free()
ator_std_init(&ator, 0);
ptr = ATOR_ALLOC(ator, 32);
strcpy(ptr, "This is a test");
ATOR_FREE(ator, ptr);
return 0;
}
pmem
Basic portable MMU based memory usage.
Handles mapping between virtual and really allocated memory.
Hides OS implementation to reserve a range of continuous address space, that is ensured to be available for allocation without using RAM.
From this reserved space the application can allocate blocks of data, actually using physical memory. This permits to have a simple memory management through arenas, pools or stacks without preallocating the full memory size.
virtual memory (address space)
+-----------------------------------------------------------------------------+
| ............................................. |
+-----------------------------------------------------------------------------+
^ reserved with pmem_reserve() ^
allocated
+-----------------------------------------------------------------------------+
| ............................................. |
| | block1 | block2 | block3 |
+-----------------------------------------------------------------------------+
^ ^^ ^^ ^
pmem_alloc() pmem_alloc() pmem_alloc()
Application is still in charge to manage allocated memory from the reserved address space, and freeing it if needed (note that freeing requires to specify the size of freed memory).
By default memory size is coded in a simple long (which is signed 64bit on
most platforms). This can be customized by defining PMEM_SIZE_TYPE before
including pmem.h. That enables usage of half the virtual address space which
is more than enough (I even started the project with a simple int limiting
memory size to 2GB).
Step 1: reserve address space
int main(void) {
pmem m = {0};
void *ptr, *next;
m = pmem_reserve(16 * 1024 * 1024); /* reserve 16MB */
/* for most usage (reserving address space for the lifetime of the program)
* pmem_release() is optional if not checking leaks during debug
*/
#ifdef _DEBUG
pmem_release(m);
#endif
return 0;
}
Step 2: allocate block of memory from reserved space
void
use_mem(pmem m) {
void *ptr, *next;
PMEM_SIZE_TYPE size1 = 16 * 1024;
PMEM_SIZE_TYPE size2 = 8 * 1024;
ptr = m.ptr;
next = ptr + size1;
pmem_alloc(ptr, size1); /* allocate 16KB at the start of reserved mem */
memset(ptr, 0, size1);
pmem_alloc(next, size2);
/* do things with memory at ptr (16K available) and next (8K available) */
/* deallocation order is not crucial */
pmem_free(ptr, size1);
pmem_free(next, size2);
}