You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
3.8 KiB
124 lines
3.8 KiB
3 months ago
|
Memory pools
|
||
|
============
|
||
|
|
||
|
You can use them for efficient allocation of large amount of small
|
||
|
memory blocks. You can use them to allocate many blocks and free them
|
||
|
all at once. They allow storing and restoring state of what is
|
||
|
allocated, growing and shrinking the last block and other tricks.
|
||
|
|
||
|
* <<defs,Definitions>>
|
||
|
* <<basic,Basic manipulation>>
|
||
|
* <<alloc,Allocation routines>>
|
||
|
* <<gbuf,Growing buffers>>
|
||
|
* <<store,Storing and restoring state>>
|
||
|
* <<string,String operations>>
|
||
|
* <<format,Formatted output>>
|
||
|
* <<examples,Examples>>
|
||
|
- <<ex_trie,String trie>>
|
||
|
- <<ex_try,Action which may fail>>
|
||
|
- <<ex_stdin,Load all data from stdin>>
|
||
|
|
||
|
!!ucw/mempool.h
|
||
|
|
||
|
[[examples]]
|
||
|
Examples
|
||
|
--------
|
||
|
|
||
|
You can find few examples of mempools use. But their actual use is
|
||
|
limited only by your fantasy.
|
||
|
|
||
|
[[ex_trie]]
|
||
|
String trie
|
||
|
~~~~~~~~~~~
|
||
|
|
||
|
There are two advantages for a trie to use a mempool. One, it has less
|
||
|
overhead than malloc (with the cost you can not free the blocks one by
|
||
|
one as you allocated them). Second is freeing the whole trie, you do
|
||
|
not need to walk trough it and free each node, you just
|
||
|
<<mp_flush(),flush>> the whole mempool.
|
||
|
|
||
|
struct trie_node {
|
||
|
struct trie_node *subs[256];
|
||
|
bool present;
|
||
|
};
|
||
|
|
||
|
struct trie {
|
||
|
struct trie_node root;
|
||
|
struct mempool *pool;
|
||
|
};
|
||
|
|
||
|
struct trie *trie_new(void) {
|
||
|
struct mempool *pool = mn_new(4096);
|
||
|
struct trie *result = mp_alloc_zero(pool, sizeof(*result));
|
||
|
result->pool = pool;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
void trie_insert_internal(struct trie_node *where, struct mempool *pool, const char *string) {
|
||
|
if(*string) {
|
||
|
if(!where->subs[*string])
|
||
|
where->subs[*string] = mp_alloc_zero(pool, sizeof(*where->subs[*string]));
|
||
|
trie_insert_internal(where->subs[*string], pool, string + 1);
|
||
|
} else {
|
||
|
where->present = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void trie_insert(struct trie *trie, const char *string) {
|
||
|
trie_insert_internal(&trie->root, trie->pool, string);
|
||
|
}
|
||
|
|
||
|
void trie_delete(struct trie *trie) {
|
||
|
mp_delete(trie->pool); //Free everything, including the trie structure
|
||
|
}
|
||
|
|
||
|
[[ex_try]]
|
||
|
Action which may fail
|
||
|
~~~~~~~~~~~~~~~~~~~~~
|
||
|
|
||
|
Imagine a situation where you want to load information from few files.
|
||
|
Loading of each file consists of list of actions, each can allocate
|
||
|
some memory and each can fail. If an action fails, the whole file is
|
||
|
considered invalid, you want to ignore that file and keep loading the
|
||
|
others.
|
||
|
|
||
|
The problem with memory is you want to return the already allocated
|
||
|
amount for the file which failed. You can use <<store,storing>> of
|
||
|
mempool state.
|
||
|
|
||
|
void load_file(struct mempool *pool, const char *file) {
|
||
|
struct mempool_state state;
|
||
|
mp_save(pool, &state); // Store the current state
|
||
|
struct file_data *data = mp_alloc_zero(pool, sizeof(*data));
|
||
|
if(!(
|
||
|
file_open(file, data, pool) && // Load the file
|
||
|
header_load(data, pool) &&
|
||
|
part1_load(data, pool) &&
|
||
|
part2_load(data, pool) &&
|
||
|
file_close(data) &&
|
||
|
data_link(data, pool))) // Link the loaded data into global state
|
||
|
mp_restore(pool, &state); // Failed -> return all used memory
|
||
|
}
|
||
|
|
||
|
[[ex_stdin]]
|
||
|
Load all data from stdin
|
||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
|
||
|
You may want to load all data from stdin into a memory buffer. But
|
||
|
there is the problem you do not know how many of them there is. You
|
||
|
may use mempool and it's <<gbuf,growing buffer>> feature.
|
||
|
|
||
|
This example uses libucw's own IO system, <<fastbuf:,fastbufs>>.
|
||
|
|
||
|
void *stdin_data(struct mempool *pool) {
|
||
|
struct fastbuf *fb = bopen_fd(0, NULL); // Read from stdin
|
||
|
size_t amount;
|
||
|
char *ptr = mp_start(pool, 1024);
|
||
|
while(amount = bread(fb, ptr, 1024)) { // Read a block
|
||
|
ptr += amount; // Move after it
|
||
|
ptr = mp_spread(pool, ptr, 1024); // Get space for the next block
|
||
|
}
|
||
|
bclose(fb);
|
||
|
return mp_end(pool, ptr);
|
||
|
}
|