/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999-2021 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not, see
. */
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
struct memory_cell
{
void *ptr;
void (*reclaim) (void *);
};
static void
memory_cell_destroy (void *item)
{
struct memory_cell *mcp = item;
if (!mcp->reclaim)
abort ();
else
mcp->reclaim (mcp->ptr);
free (mcp);
}
static int
memory_cell_cmp (const void *a, const void *b)
{
struct memory_cell const *ma = a;
struct memory_cell const *mb = b;
return ma->ptr != mb->ptr;
}
void
mu_sieve_register_memory (mu_sieve_machine_t mach, void *ptr,
mu_sieve_reclaim_t reclaim)
{
struct memory_cell *mcp;
if (!reclaim)
reclaim = mu_sieve_reclaim_default;
if (!mach->memory_pool)
{
if (mu_list_create (&mach->memory_pool))
mu_sieve_abort (mach);
mu_list_set_destroy_item (mach->memory_pool, memory_cell_destroy);
mu_list_set_comparator (mach->memory_pool, memory_cell_cmp);
}
mcp = malloc (sizeof (*mcp));
if (!mcp)
mu_sieve_abort (mach);
mcp->ptr = ptr;
mcp->reclaim = reclaim;
if (mu_list_append (mach->memory_pool, mcp))
{
memory_cell_destroy (mcp);
mu_sieve_abort (mach);
}
}
void
mu_sieve_free (mu_sieve_machine_t mach, void *ptr)
{
int rc;
struct memory_cell mcell;
if (!ptr)
return;
mcell.ptr = ptr;
rc = mu_list_remove (mach->memory_pool, &mcell);
if (rc)
{
mu_sieve_error (mach, _("INTERNAL ERROR: trying to free unregistered memory pointer"));
abort ();
}
}
void *
mu_sieve_alloc_memory (mu_sieve_machine_t mach, size_t size,
mu_sieve_reclaim_t recfun)
{
char *p = malloc (size);
if (!p)
{
mu_sieve_error (mach, "%s", mu_strerror (errno));
mu_sieve_abort (mach);
}
mu_sieve_register_memory (mach, p, recfun);
return p;
}
void *
mu_sieve_malloc (mu_sieve_machine_t mach, size_t size)
{
return mu_sieve_alloc_memory (mach, size, mu_sieve_reclaim_default);
}
void *
mu_sieve_calloc (mu_sieve_machine_t mach, size_t nmemb, size_t size)
{
char *p = calloc (nmemb, size);
if (!p)
{
mu_sieve_error (mach, "%s", mu_strerror (errno));
mu_sieve_abort (mach);
}
mu_sieve_register_memory (mach, p, mu_sieve_reclaim_default);
return p;
}
char *
mu_sieve_strdup (mu_sieve_machine_t mach, char const *str)
{
size_t len;
char *p;
if (!str)
return NULL;
len = strlen (str);
p = mu_sieve_malloc (mach, len + 1);
memcpy (p, str, len);
p[len] = 0;
return p;
}
void *
mu_sieve_realloc (mu_sieve_machine_t mach, void *ptr, size_t size)
{
int rc;
struct memory_cell mcell, *mcp;
if (!ptr)
return mu_sieve_malloc (mach, size);
mcell.ptr = ptr;
rc = mu_list_locate (mach->memory_pool, &mcell, (void **)&mcp);
if (rc == MU_ERR_NOENT)
{
mu_sieve_error (mach, _("INTERNAL ERROR: trying to reallocate unregistered memory pointer"));
abort ();
}
else if (rc)
{
mu_sieve_error (mach, _("error reallocating memory: %s"),
mu_strerror (rc));
mu_sieve_abort (mach);
}
ptr = realloc (mcp->ptr, size);
if (!ptr)
{
mu_sieve_error (mach, _("error reallocating memory: %s"),
mu_strerror (errno));
mu_sieve_abort (mach);
}
mcp->ptr = ptr;
return ptr;
}
void
mu_sieve_reclaim_default (void *p)
{
free (p);
}
void
mu_sieve_reclaim_value (void *p)
{
if (!p)
return;
/* For now, the same as _default. Will change in the future */
free (p);
}
/* Based on gnulib's x2nrealloc */
void
mu_i_sv_2nrealloc (mu_sieve_machine_t mach, void **pptr, size_t *pnmemb,
size_t size)
{
void *ptr = *pptr;
size_t nmemb = *pnmemb;
if (!ptr)
{
if (!nmemb)
{
/* Initial allocation size */
nmemb = 16;
}
}
else
{
/* Set NMEMB = floor (1.5 * NMEMB) + 1 so that progress is made even
if NMEMB == 0.
Check for overflow, so that NMEMB * SIZE stays in size_t range.
The check may be slightly conservative, but an exact check isn't
worth the trouble. */
if ((size_t) -1 / 3 * 2 / size <= nmemb)
{
mu_diag_at_locus_range (MU_LOG_ERROR, &mach->locus,
_("requested too much memory %zu * %zu"),
nmemb, size);
mu_sieve_abort (mach);
}
nmemb += nmemb / 2 + 1;
}
ptr = mu_sieve_realloc (mach, ptr, nmemb * size);
*pptr = ptr;
*pnmemb = nmemb;
}
size_t
mu_i_sv_id_num (mu_sieve_machine_t mach, char const *name)
{
size_t i;
char *p;
if (!name)
abort ();
for (i = 0; i < mach->idcount; i++)
{
if (strcmp (mach->idspace[i], name) == 0)
return i;
}
if (mach->idcount == mach->idmax)
{
mu_i_sv_2nrealloc (mach,
(void **) &mach->idspace,
&mach->idmax,
sizeof mach->idspace[0]);
}
p = mu_sieve_strdup (mach, name);
mach->idspace[mach->idcount] = p;
return mach->idcount++;
}
char *
mu_i_sv_id_str (mu_sieve_machine_t mach, size_t n)
{
if (n >= mach->idcount)
abort ();
return mach->idspace[n];
}
void
mu_i_sv_free_idspace (mu_sieve_machine_t mach)
{
size_t i;
for (i = 0; i < mach->idcount; i++)
mu_sieve_free (mach, mach->idspace[i]);
mach->idcount = 0;
}