1998-05-03 16:43:39 +00:00
|
|
|
/*
|
|
|
|
* BIRD Resource Manager
|
|
|
|
*
|
2000-06-05 11:41:41 +00:00
|
|
|
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
|
1998-05-03 16:43:39 +00:00
|
|
|
*
|
|
|
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2009-09-17 15:52:36 +00:00
|
|
|
#include <stdint.h>
|
1998-05-03 16:43:39 +00:00
|
|
|
|
|
|
|
#include "nest/bird.h"
|
|
|
|
#include "lib/resource.h"
|
2000-03-31 23:30:21 +00:00
|
|
|
#include "lib/string.h"
|
2021-11-12 21:58:40 +00:00
|
|
|
#include "lib/rcu.h"
|
1998-05-03 16:43:39 +00:00
|
|
|
|
2000-06-05 11:41:41 +00:00
|
|
|
/**
|
|
|
|
* DOC: Resource pools
|
|
|
|
*
|
|
|
|
* Resource pools (&pool) are just containers holding a list of
|
|
|
|
* other resources. Freeing a pool causes all the listed resources
|
|
|
|
* to be freed as well. Each existing &resource is linked to some pool
|
|
|
|
* except for a root pool which isn't linked anywhere, so all the
|
|
|
|
* resources form a tree structure with internal nodes corresponding
|
|
|
|
* to pools and leaves being the other resources.
|
|
|
|
*
|
|
|
|
* Example: Almost all modules of BIRD have their private pool which
|
|
|
|
* is freed upon shutdown of the module.
|
|
|
|
*/
|
|
|
|
|
1998-05-03 16:43:39 +00:00
|
|
|
struct pool {
|
|
|
|
resource r;
|
|
|
|
list inside;
|
2021-08-31 22:46:46 +00:00
|
|
|
struct pool_pages *pages;
|
2015-09-17 15:15:30 +00:00
|
|
|
const char *name;
|
1998-05-03 16:43:39 +00:00
|
|
|
};
|
|
|
|
|
2021-08-31 22:46:46 +00:00
|
|
|
struct pool_pages {
|
|
|
|
uint free;
|
|
|
|
uint used;
|
|
|
|
void *ptr[0];
|
|
|
|
};
|
|
|
|
|
|
|
|
#define POOL_PAGES_MAX ((page_size - sizeof(struct pool_pages)) / sizeof (void *))
|
|
|
|
|
1998-05-24 14:46:20 +00:00
|
|
|
static void pool_dump(resource *);
|
|
|
|
static void pool_free(resource *);
|
2000-05-08 22:33:38 +00:00
|
|
|
static resource *pool_lookup(resource *, unsigned long);
|
2010-06-02 20:20:40 +00:00
|
|
|
static size_t pool_memsize(resource *P);
|
1998-05-03 16:43:39 +00:00
|
|
|
|
|
|
|
static struct resclass pool_class = {
|
|
|
|
"Pool",
|
|
|
|
sizeof(pool),
|
|
|
|
pool_free,
|
2000-05-08 22:33:38 +00:00
|
|
|
pool_dump,
|
2010-06-02 20:20:40 +00:00
|
|
|
pool_lookup,
|
|
|
|
pool_memsize
|
1998-05-03 16:43:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
pool root_pool;
|
|
|
|
|
2021-08-31 22:46:46 +00:00
|
|
|
void *alloc_sys_page(void);
|
2021-11-11 15:25:59 +00:00
|
|
|
int free_sys_page(void *);
|
2021-08-31 22:46:46 +00:00
|
|
|
|
1998-05-03 16:43:39 +00:00
|
|
|
static int indent;
|
|
|
|
|
2000-06-05 11:41:41 +00:00
|
|
|
/**
|
|
|
|
* rp_new - create a resource pool
|
|
|
|
* @p: parent pool
|
|
|
|
* @name: pool name (to be included in debugging dumps)
|
|
|
|
*
|
|
|
|
* rp_new() creates a new resource pool inside the specified
|
|
|
|
* parent pool.
|
|
|
|
*/
|
1998-05-03 16:43:39 +00:00
|
|
|
pool *
|
2015-09-17 15:15:30 +00:00
|
|
|
rp_new(pool *p, const char *name)
|
1998-05-03 16:43:39 +00:00
|
|
|
{
|
|
|
|
pool *z = ralloc(p, &pool_class);
|
1999-03-29 20:14:33 +00:00
|
|
|
z->name = name;
|
1998-05-03 16:43:39 +00:00
|
|
|
init_list(&z->inside);
|
|
|
|
return z;
|
|
|
|
}
|
|
|
|
|
1998-05-24 14:46:20 +00:00
|
|
|
static void
|
1998-05-03 16:43:39 +00:00
|
|
|
pool_free(resource *P)
|
|
|
|
{
|
|
|
|
pool *p = (pool *) P;
|
|
|
|
resource *r, *rr;
|
|
|
|
|
|
|
|
r = HEAD(p->inside);
|
|
|
|
while (rr = (resource *) r->n.next)
|
|
|
|
{
|
|
|
|
r->class->free(r);
|
|
|
|
xfree(r);
|
|
|
|
r = rr;
|
|
|
|
}
|
2021-08-31 22:46:46 +00:00
|
|
|
|
|
|
|
if (p->pages)
|
|
|
|
{
|
|
|
|
ASSERT_DIE(!p->pages->used);
|
2021-11-11 15:25:59 +00:00
|
|
|
|
|
|
|
for (uint i = 0; i < p->pages->free; i++)
|
2021-08-31 22:46:46 +00:00
|
|
|
free_sys_page(p->pages->ptr[i]);
|
2021-11-11 15:25:59 +00:00
|
|
|
|
2021-08-31 22:46:46 +00:00
|
|
|
free_sys_page(p->pages);
|
|
|
|
}
|
1998-05-03 16:43:39 +00:00
|
|
|
}
|
|
|
|
|
1998-05-24 14:46:20 +00:00
|
|
|
static void
|
1998-05-03 16:43:39 +00:00
|
|
|
pool_dump(resource *P)
|
|
|
|
{
|
|
|
|
pool *p = (pool *) P;
|
|
|
|
resource *r;
|
|
|
|
|
1999-03-29 20:14:33 +00:00
|
|
|
debug("%s\n", p->name);
|
1998-05-03 16:43:39 +00:00
|
|
|
indent += 3;
|
|
|
|
WALK_LIST(r, p->inside)
|
|
|
|
rdump(r);
|
|
|
|
indent -= 3;
|
|
|
|
}
|
|
|
|
|
2010-06-02 20:20:40 +00:00
|
|
|
static size_t
|
|
|
|
pool_memsize(resource *P)
|
|
|
|
{
|
|
|
|
pool *p = (pool *) P;
|
|
|
|
resource *r;
|
|
|
|
size_t sum = sizeof(pool) + ALLOC_OVERHEAD;
|
|
|
|
|
|
|
|
WALK_LIST(r, p->inside)
|
|
|
|
sum += rmemsize(r);
|
|
|
|
|
2021-08-31 22:46:46 +00:00
|
|
|
if (p->pages)
|
|
|
|
sum += page_size * (p->pages->used + p->pages->free + 1);
|
|
|
|
|
2010-06-02 20:20:40 +00:00
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
|
2000-05-08 22:33:38 +00:00
|
|
|
static resource *
|
|
|
|
pool_lookup(resource *P, unsigned long a)
|
|
|
|
{
|
|
|
|
pool *p = (pool *) P;
|
|
|
|
resource *r, *q;
|
|
|
|
|
|
|
|
WALK_LIST(r, p->inside)
|
|
|
|
if (r->class->lookup && (q = r->class->lookup(r, a)))
|
|
|
|
return q;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-05-31 18:47:19 +00:00
|
|
|
/**
|
|
|
|
* rmove - move a resource
|
|
|
|
* @res: resource
|
|
|
|
* @p: pool to move the resource to
|
|
|
|
*
|
|
|
|
* rmove() moves a resource from one pool to another.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void rmove(void *res, pool *p)
|
|
|
|
{
|
|
|
|
resource *r = res;
|
|
|
|
|
|
|
|
if (r)
|
|
|
|
{
|
|
|
|
if (r->n.next)
|
|
|
|
rem_node(&r->n);
|
|
|
|
add_tail(&p->inside, &r->n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-06-05 11:41:41 +00:00
|
|
|
/**
|
|
|
|
* rfree - free a resource
|
|
|
|
* @res: resource
|
|
|
|
*
|
|
|
|
* rfree() frees the given resource and all information associated
|
|
|
|
* with it. In case it's a resource pool, it also frees all the objects
|
|
|
|
* living inside the pool.
|
|
|
|
*
|
|
|
|
* It works by calling a class-specific freeing function.
|
|
|
|
*/
|
1998-05-03 16:43:39 +00:00
|
|
|
void
|
|
|
|
rfree(void *res)
|
|
|
|
{
|
|
|
|
resource *r = res;
|
|
|
|
|
2014-02-06 16:46:01 +00:00
|
|
|
if (!r)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (r->n.next)
|
|
|
|
rem_node(&r->n);
|
|
|
|
r->class->free(r);
|
2016-03-22 12:35:40 +00:00
|
|
|
r->class = NULL;
|
2014-02-06 16:46:01 +00:00
|
|
|
xfree(r);
|
1998-05-03 16:43:39 +00:00
|
|
|
}
|
|
|
|
|
2000-06-05 11:41:41 +00:00
|
|
|
/**
|
|
|
|
* rdump - dump a resource
|
|
|
|
* @res: resource
|
|
|
|
*
|
|
|
|
* This function prints out all available information about the given
|
|
|
|
* resource to the debugging output.
|
|
|
|
*
|
|
|
|
* It works by calling a class-specific dump function.
|
|
|
|
*/
|
1998-05-03 16:43:39 +00:00
|
|
|
void
|
|
|
|
rdump(void *res)
|
|
|
|
{
|
|
|
|
char x[16];
|
|
|
|
resource *r = res;
|
|
|
|
|
2008-12-25 10:55:27 +00:00
|
|
|
bsprintf(x, "%%%ds%%p ", indent);
|
|
|
|
debug(x, "", r);
|
1998-05-03 16:43:39 +00:00
|
|
|
if (r)
|
|
|
|
{
|
1999-03-29 20:14:33 +00:00
|
|
|
debug("%s ", r->class->name);
|
1998-05-03 16:43:39 +00:00
|
|
|
r->class->dump(r);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
debug("NULL\n");
|
|
|
|
}
|
|
|
|
|
2010-06-02 20:20:40 +00:00
|
|
|
size_t
|
|
|
|
rmemsize(void *res)
|
|
|
|
{
|
|
|
|
resource *r = res;
|
|
|
|
if (!r)
|
|
|
|
return 0;
|
|
|
|
if (!r->class->memsize)
|
|
|
|
return r->class->size + ALLOC_OVERHEAD;
|
|
|
|
return r->class->memsize(r);
|
|
|
|
}
|
|
|
|
|
2000-06-05 11:41:41 +00:00
|
|
|
/**
|
|
|
|
* ralloc - create a resource
|
|
|
|
* @p: pool to create the resource in
|
|
|
|
* @c: class of the new resource
|
|
|
|
*
|
|
|
|
* This function is called by the resource classes to create a new
|
|
|
|
* resource of the specified class and link it to the given pool.
|
2009-09-04 09:24:08 +00:00
|
|
|
* Allocated memory is zeroed. Size of the resource structure is taken
|
|
|
|
* from the @size field of the &resclass.
|
2000-06-05 11:41:41 +00:00
|
|
|
*/
|
1998-05-03 16:43:39 +00:00
|
|
|
void *
|
|
|
|
ralloc(pool *p, struct resclass *c)
|
|
|
|
{
|
|
|
|
resource *r = xmalloc(c->size);
|
2009-09-04 09:24:08 +00:00
|
|
|
bzero(r, c->size);
|
1998-05-03 16:43:39 +00:00
|
|
|
|
|
|
|
r->class = c;
|
2013-09-16 21:57:40 +00:00
|
|
|
if (p)
|
|
|
|
add_tail(&p->inside, &r->n);
|
1998-05-03 16:43:39 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2000-06-05 11:41:41 +00:00
|
|
|
/**
|
|
|
|
* rlookup - look up a memory location
|
|
|
|
* @a: memory address
|
|
|
|
*
|
|
|
|
* This function examines all existing resources to see whether
|
|
|
|
* the address @a is inside any resource. It's used for debugging
|
|
|
|
* purposes only.
|
|
|
|
*
|
|
|
|
* It works by calling a class-specific lookup function for each
|
|
|
|
* resource.
|
|
|
|
*/
|
2000-05-08 22:33:38 +00:00
|
|
|
void
|
|
|
|
rlookup(unsigned long a)
|
|
|
|
{
|
|
|
|
resource *r;
|
|
|
|
|
|
|
|
debug("Looking up %08lx\n", a);
|
|
|
|
if (r = pool_lookup(&root_pool.r, a))
|
|
|
|
rdump(r);
|
|
|
|
else
|
|
|
|
debug("Not found.\n");
|
|
|
|
}
|
|
|
|
|
2000-06-05 11:41:41 +00:00
|
|
|
/**
|
|
|
|
* resource_init - initialize the resource manager
|
|
|
|
*
|
|
|
|
* This function is called during BIRD startup. It initializes
|
|
|
|
* all data structures of the resource manager and creates the
|
|
|
|
* root pool.
|
|
|
|
*/
|
1998-05-03 16:43:39 +00:00
|
|
|
void
|
|
|
|
resource_init(void)
|
|
|
|
{
|
2021-11-12 21:58:40 +00:00
|
|
|
rcu_init();
|
|
|
|
|
1998-05-03 16:43:39 +00:00
|
|
|
root_pool.r.class = &pool_class;
|
1998-05-26 21:37:37 +00:00
|
|
|
root_pool.name = "Root";
|
1998-05-03 16:43:39 +00:00
|
|
|
init_list(&root_pool.inside);
|
|
|
|
}
|
|
|
|
|
2000-06-05 11:41:41 +00:00
|
|
|
/**
|
|
|
|
* DOC: Memory blocks
|
|
|
|
*
|
|
|
|
* Memory blocks are pieces of contiguous allocated memory.
|
|
|
|
* They are a bit non-standard since they are represented not by a pointer
|
|
|
|
* to &resource, but by a void pointer to the start of data of the
|
|
|
|
* memory block. All memory block functions know how to locate the header
|
|
|
|
* given the data pointer.
|
|
|
|
*
|
|
|
|
* Example: All "unique" data structures such as hash tables are allocated
|
|
|
|
* as memory blocks.
|
1998-05-03 16:43:39 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
struct mblock {
|
|
|
|
resource r;
|
|
|
|
unsigned size;
|
2009-07-06 17:07:01 +00:00
|
|
|
uintptr_t data_align[0];
|
1998-05-03 16:43:39 +00:00
|
|
|
byte data[0];
|
|
|
|
};
|
|
|
|
|
2004-06-05 09:27:02 +00:00
|
|
|
static void mbl_free(resource *r UNUSED)
|
1998-05-03 16:43:39 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
1998-05-24 14:46:20 +00:00
|
|
|
static void mbl_debug(resource *r)
|
1998-05-03 16:43:39 +00:00
|
|
|
{
|
|
|
|
struct mblock *m = (struct mblock *) r;
|
|
|
|
|
|
|
|
debug("(size=%d)\n", m->size);
|
|
|
|
}
|
|
|
|
|
2000-05-08 22:33:38 +00:00
|
|
|
static resource *
|
|
|
|
mbl_lookup(resource *r, unsigned long a)
|
|
|
|
{
|
|
|
|
struct mblock *m = (struct mblock *) r;
|
|
|
|
|
|
|
|
if ((unsigned long) m->data <= a && (unsigned long) m->data + m->size > a)
|
|
|
|
return r;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-06-02 20:20:40 +00:00
|
|
|
static size_t
|
|
|
|
mbl_memsize(resource *r)
|
|
|
|
{
|
|
|
|
struct mblock *m = (struct mblock *) r;
|
|
|
|
return ALLOC_OVERHEAD + sizeof(struct mblock) + m->size;
|
|
|
|
}
|
|
|
|
|
1998-05-24 14:46:20 +00:00
|
|
|
static struct resclass mb_class = {
|
1998-05-03 16:43:39 +00:00
|
|
|
"Memory",
|
|
|
|
0,
|
|
|
|
mbl_free,
|
|
|
|
mbl_debug,
|
2010-06-02 20:20:40 +00:00
|
|
|
mbl_lookup,
|
|
|
|
mbl_memsize
|
1998-05-03 16:43:39 +00:00
|
|
|
};
|
|
|
|
|
2000-06-05 11:41:41 +00:00
|
|
|
/**
|
|
|
|
* mb_alloc - allocate a memory block
|
|
|
|
* @p: pool
|
|
|
|
* @size: size of the block
|
|
|
|
*
|
|
|
|
* mb_alloc() allocates memory of a given size and creates
|
|
|
|
* a memory block resource representing this memory chunk
|
|
|
|
* in the pool @p.
|
|
|
|
*
|
|
|
|
* Please note that mb_alloc() returns a pointer to the memory
|
|
|
|
* chunk, not to the resource, hence you have to free it using
|
|
|
|
* mb_free(), not rfree().
|
|
|
|
*/
|
1998-05-03 16:43:39 +00:00
|
|
|
void *
|
|
|
|
mb_alloc(pool *p, unsigned size)
|
|
|
|
{
|
|
|
|
struct mblock *b = xmalloc(sizeof(struct mblock) + size);
|
|
|
|
|
|
|
|
b->r.class = &mb_class;
|
2019-08-17 12:18:41 +00:00
|
|
|
b->r.n = (node) {};
|
1998-05-03 16:43:39 +00:00
|
|
|
add_tail(&p->inside, &b->r.n);
|
|
|
|
b->size = size;
|
|
|
|
return b->data;
|
|
|
|
}
|
|
|
|
|
2000-06-05 11:41:41 +00:00
|
|
|
/**
|
|
|
|
* mb_allocz - allocate and clear a memory block
|
|
|
|
* @p: pool
|
|
|
|
* @size: size of the block
|
|
|
|
*
|
|
|
|
* mb_allocz() allocates memory of a given size, initializes it to
|
|
|
|
* zeroes and creates a memory block resource representing this memory
|
|
|
|
* chunk in the pool @p.
|
|
|
|
*
|
2012-04-22 12:03:07 +00:00
|
|
|
* Please note that mb_allocz() returns a pointer to the memory
|
2000-06-05 11:41:41 +00:00
|
|
|
* chunk, not to the resource, hence you have to free it using
|
|
|
|
* mb_free(), not rfree().
|
|
|
|
*/
|
1999-03-04 11:36:26 +00:00
|
|
|
void *
|
|
|
|
mb_allocz(pool *p, unsigned size)
|
|
|
|
{
|
|
|
|
void *x = mb_alloc(p, size);
|
|
|
|
bzero(x, size);
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
2009-06-10 21:45:08 +00:00
|
|
|
/**
|
|
|
|
* mb_realloc - reallocate a memory block
|
|
|
|
* @m: memory block
|
|
|
|
* @size: new size of the block
|
|
|
|
*
|
|
|
|
* mb_realloc() changes the size of the memory block @m to a given size.
|
|
|
|
* The contents will be unchanged to the minimum of the old and new sizes;
|
2013-09-10 10:09:36 +00:00
|
|
|
* newly allocated memory will be uninitialized. Contrary to realloc()
|
|
|
|
* behavior, @m must be non-NULL, because the resource pool is inherited
|
|
|
|
* from it.
|
2009-06-10 21:45:08 +00:00
|
|
|
*
|
|
|
|
* Like mb_alloc(), mb_realloc() also returns a pointer to the memory
|
2013-09-10 10:09:36 +00:00
|
|
|
* chunk, not to the resource, hence you have to free it using
|
2009-06-10 21:45:08 +00:00
|
|
|
* mb_free(), not rfree().
|
|
|
|
*/
|
|
|
|
void *
|
2013-09-10 10:09:36 +00:00
|
|
|
mb_realloc(void *m, unsigned size)
|
2009-06-10 21:45:08 +00:00
|
|
|
{
|
2016-03-22 12:35:40 +00:00
|
|
|
struct mblock *b = SKIP_BACK(struct mblock, data, m);
|
2009-06-10 21:45:08 +00:00
|
|
|
|
2016-03-22 12:35:40 +00:00
|
|
|
b = xrealloc(b, sizeof(struct mblock) + size);
|
2019-08-19 12:36:51 +00:00
|
|
|
update_node(&b->r.n);
|
2009-06-10 21:45:08 +00:00
|
|
|
b->size = size;
|
|
|
|
return b->data;
|
|
|
|
}
|
|
|
|
|
2021-03-30 14:03:33 +00:00
|
|
|
/**
|
|
|
|
* mb_move - move a memory block
|
|
|
|
* @m: memory block
|
|
|
|
* @p: target pool
|
|
|
|
*
|
|
|
|
* mb_move() moves the given memory block to another pool in the same way
|
|
|
|
* as rmove() moves a plain resource.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
mb_move(void *m, pool *p)
|
|
|
|
{
|
|
|
|
struct mblock *b = SKIP_BACK(struct mblock, data, m);
|
|
|
|
rmove(b, p);
|
|
|
|
}
|
|
|
|
|
2009-06-10 21:45:08 +00:00
|
|
|
|
2000-06-05 11:41:41 +00:00
|
|
|
/**
|
|
|
|
* mb_free - free a memory block
|
|
|
|
* @m: memory block
|
|
|
|
*
|
|
|
|
* mb_free() frees all memory associated with the block @m.
|
|
|
|
*/
|
1998-05-03 16:43:39 +00:00
|
|
|
void
|
|
|
|
mb_free(void *m)
|
|
|
|
{
|
2014-02-06 16:46:01 +00:00
|
|
|
if (!m)
|
|
|
|
return;
|
|
|
|
|
1998-05-03 16:43:39 +00:00
|
|
|
struct mblock *b = SKIP_BACK(struct mblock, data, m);
|
|
|
|
rfree(b);
|
|
|
|
}
|
2009-06-10 21:45:08 +00:00
|
|
|
|
2021-08-31 22:46:46 +00:00
|
|
|
void *
|
|
|
|
alloc_page(pool *p)
|
|
|
|
{
|
|
|
|
if (!p->pages)
|
|
|
|
{
|
|
|
|
p->pages = alloc_sys_page();
|
|
|
|
p->pages->free = 0;
|
|
|
|
p->pages->used = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
p->pages->used++;
|
|
|
|
|
|
|
|
if (p->pages->free)
|
|
|
|
{
|
|
|
|
void *ptr = p->pages->ptr[--p->pages->free];
|
|
|
|
bzero(ptr, page_size);
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return alloc_sys_page();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
free_page(pool *p, void *ptr)
|
|
|
|
{
|
|
|
|
ASSERT_DIE(p->pages);
|
|
|
|
p->pages->used--;
|
|
|
|
|
2021-11-11 15:25:59 +00:00
|
|
|
ASSERT_DIE(p->pages->free <= POOL_PAGES_MAX);
|
|
|
|
|
|
|
|
if (p->pages->free == POOL_PAGES_MAX)
|
|
|
|
{
|
|
|
|
const unsigned long keep = POOL_PAGES_MAX / 4;
|
|
|
|
|
|
|
|
for (uint i = keep; i < p->pages->free; i++)
|
|
|
|
free_sys_page(p->pages->ptr[i]);
|
|
|
|
|
|
|
|
p->pages->free = keep;
|
|
|
|
}
|
|
|
|
|
|
|
|
p->pages->ptr[p->pages->free++] = ptr;
|
2021-08-31 22:46:46 +00:00
|
|
|
}
|
2013-09-10 10:09:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
#define STEP_UP(x) ((x) + (x)/2 + 4)
|
|
|
|
|
|
|
|
void
|
|
|
|
buffer_realloc(void **buf, unsigned *size, unsigned need, unsigned item_size)
|
|
|
|
{
|
|
|
|
unsigned nsize = MIN(*size, need);
|
|
|
|
|
|
|
|
while (nsize < need)
|
|
|
|
nsize = STEP_UP(nsize);
|
|
|
|
|
2013-09-16 21:57:40 +00:00
|
|
|
*buf = mb_realloc(*buf, nsize * item_size);
|
2013-09-10 10:09:36 +00:00
|
|
|
*size = nsize;
|
|
|
|
}
|