Embperl
view release on metacpan or search on metacpan
/* Get a completely new block from the system pool. Note that we rely on
malloc() to provide aligned memory. */
static union block_hdr *malloc_block(int size)
{
union block_hdr *blok;
#ifdef ALLOC_DEBUG
/* make some room at the end which we'll fill and expect to be
* always filled
*/
size += CLICK_SZ;
#endif
#ifdef ALLOC_STATS
++num_malloc_calls;
num_malloc_bytes += size + sizeof(union block_hdr);
#endif
blok = (union block_hdr *) malloc(size + sizeof(union block_hdr));
if (blok == NULL) {
/*fprintf(stderr, "Ouch! malloc failed in malloc_block()\n");*/
/* mmmh, Perl overrides stderr, so it won't work here!!! bad... */
printf("Ouch! malloc failed in malloc_block()\n");
exit(1);
}
debug_fill(blok, size + sizeof(union block_hdr));
blok->h.next = NULL;
blok->h.first_avail = (char *) (blok + 1);
blok->h.endp = size + blok->h.first_avail;
#ifdef ALLOC_DEBUG
blok->h.endp -= CLICK_SZ;
#endif
#ifdef POOL_DEBUG
blok->h.global_next = global_block_list;
global_block_list = blok;
blok->h.owning_pool = NULL;
#endif
return blok;
}
#if defined(ALLOC_DEBUG) && !defined(ALLOC_USE_MALLOC)
static void chk_on_blk_list(union block_hdr *blok, union block_hdr *free_blk)
{
debug_verify_filled(blok->h.endp, blok->h.endp + CLICK_SZ,
"Ouch! Someone trounced the padding at the end of a block!\n");
while (free_blk) {
if (free_blk == blok) {
fprintf(stderr, "Ouch! Freeing free block\n");
abort();
exit(1);
}
free_blk = free_blk->h.next;
}
}
#else
#define chk_on_blk_list(_x, _y)
#endif
/* Free a chain of blocks --- must be called with alarms blocked. */
static void free_blocks(union block_hdr *blok)
{
#ifdef ALLOC_USE_MALLOC
union block_hdr *next;
for (; blok; blok = next) {
next = blok->h.next;
free(blok);
}
#else
#ifdef ALLOC_STATS
unsigned num_blocks;
#endif
/* First, put new blocks at the head of the free list ---
* we'll eventually bash the 'next' pointer of the last block
* in the chain to point to the free blocks we already had.
*/
union block_hdr *old_free_list;
if (blok == NULL)
return; /* Sanity check --- freeing empty pool? */
ep_acquire_mutex(alloc_mutex);
old_free_list = block_freelist;
block_freelist = blok;
/*
* Next, adjust first_avail pointers of each block --- have to do it
* sooner or later, and it simplifies the search in new_block to do it
* now.
*/
#ifdef ALLOC_STATS
num_blocks = 1;
#endif
while (blok->h.next != NULL) {
#ifdef ALLOC_STATS
++num_blocks;
#endif
chk_on_blk_list(blok, old_free_list);
blok->h.first_avail = (char *) (blok + 1);
debug_fill(blok->h.first_avail, blok->h.endp - blok->h.first_avail);
#ifdef POOL_DEBUG
blok->h.owning_pool = FREE_POOL;
#endif
blok = blok->h.next;
}
chk_on_blk_list(blok, old_free_list);
blok->h.first_avail = (char *) (blok + 1);
debug_fill(blok->h.first_avail, blok->h.endp - blok->h.first_avail);
#ifdef POOL_DEBUG
blok->h.owning_pool = FREE_POOL;
#endif
/* Finally, reset next pointer to get the old free blocks back */
blok->h.next = old_free_list;
#ifdef ALLOC_STATS
if (num_blocks > max_blocks_in_one_free) {
max_blocks_in_one_free = num_blocks;
}
++num_free_blocks_calls;
num_blocks_freed += num_blocks;
#endif
ep_release_mutex(alloc_mutex);
#endif
}
/* Get a new block, from our own free list if possible, from the system
* if necessary. Must be called with alarms blocked.
*/
static union block_hdr *new_block(int min_size)
{
union block_hdr **lastptr = &block_freelist;
union block_hdr *blok = block_freelist;
/* First, see if we have anything of the required size
* on the free list...
*/
while (blok != NULL) {
if (min_size + BLOCK_MINFREE <= blok->h.endp - blok->h.first_avail) {
*lastptr = blok->h.next;
blok->h.next = NULL;
debug_verify_filled(blok->h.first_avail, blok->h.endp,
"Ouch! Someone trounced a block on the free list!\n");
return blok;
}
else {
lastptr = &blok->h.next;
blok = blok->h.next;
}
}
/* Nope. */
min_size += BLOCK_MINFREE;
blok = malloc_block((min_size > BLOCK_MINALLOC) ? min_size : BLOCK_MINALLOC);
return blok;
}
/* Accounting */
static long bytes_in_block_list(union block_hdr *blok)
{
long size = 0;
while (blok) {
size += blok->h.endp - (char *) (blok + 1);
blok = blok->h.next;
}
return size;
}
/*****************************************************************
*
* tMemPool internals and management...
* NB that subprocesses are not handled by the generic cleanup code,
* basically because we don't want cleanups for multiple subprocesses
* to result in multiple three-second pauses.
*/
struct process_chain;
struct cleanup;
/* static void run_cleanups(struct cleanup *); */
EP_API_EXPORT(char *) ep_pstrndup(struct tMemPool *a, const char *s, int n)
{
char *res;
if (s == NULL)
return NULL;
res = ep_palloc(a, n + 1);
memcpy(res, s, n);
res[n] = '\0';
return res;
}
EP_API_EXPORT_NONSTD(char *) ep_pstrcat(tMemPool *a,...)
{
char *cp, *argp, *res;
/* Pass one --- find length of required string */
int len = 0;
va_list adummy;
va_start(adummy, a);
while ((cp = va_arg(adummy, char *)) != NULL)
len += strlen(cp);
va_end(adummy);
/* Allocate the required string */
res = (char *) ep_palloc(a, len + 1);
cp = res;
*cp = '\0';
/* Pass two --- copy the argument strings into the result space */
va_start(adummy, a);
while ((argp = va_arg(adummy, char *)) != NULL) {
strcpy(cp, argp);
cp += strlen(argp);
}
va_end(adummy);
/* Return the result string */
return res;
}
#ifdef EPSPRINTF
/* ep_psprintf is implemented by writing directly into the current
* block of the pool, starting right at first_avail. If there's
* insufficient room, then a new block is allocated and the earlier
* output is copied over. The new block isn't linked into the pool
* until all the output is done.
*
* Note that this is completely safe because nothing else can
* allocate in this tMemPool while ep_psprintf is running. alarms are
* blocked, and the only thing outside of alloc.c that's invoked
* is ep_vformatter -- which was purposefully written to be
* self-contained with no callouts.
*/
struct psprintf_data {
ep_vformatter_buff vbuff;
#ifdef ALLOC_USE_MALLOC
char *base;
#else
union block_hdr *blok;
int got_a_new_block;
#endif
};
static int psprintf_flush(ep_vformatter_buff *vbuff)
{
struct psprintf_data *ps = (struct psprintf_data *)vbuff;
#ifdef ALLOC_USE_MALLOC
int size;
char *ptr;
size = (char *)ps->vbuff.curpos - ps->base;
ptr = realloc(ps->base, 2*size);
if (ptr == NULL) {
fputs("Ouch! Out of memory!\n", stderr);
exit(1);
}
ps->base = ptr;
ps->vbuff.curpos = ptr + size;
ps->vbuff.endpos = ptr + 2*size - 1;
return 0;
#else
union block_hdr *blok;
union block_hdr *nblok;
size_t cur_len;
char *strp;
blok = ps->blok;
strp = ps->vbuff.curpos;
cur_len = strp - blok->h.first_avail;
/* must try another blok */
(void) ep_acquire_mutex(alloc_mutex);
nblok = new_block(2 * cur_len);
(void) ep_release_mutex(alloc_mutex);
memcpy(nblok->h.first_avail, blok->h.first_avail, cur_len);
ps->vbuff.curpos = nblok->h.first_avail + cur_len;
/* save a byte for the NUL terminator */
ps->vbuff.endpos = nblok->h.endp - 1;
/* did we allocate the current blok? if so free it up */
if (ps->got_a_new_block) {
debug_fill(blok->h.first_avail, blok->h.endp - blok->h.first_avail);
(void) ep_acquire_mutex(alloc_mutex);
blok->h.next = block_freelist;
block_freelist = blok;
(void) ep_release_mutex(alloc_mutex);
}
ps->blok = nblok;
ps->got_a_new_block = 1;
( run in 0.608 second using v1.01-cache-2.11-cpan-f56aa216473 )