/* Copyright (c) 2002-2003 krzYszcz and others.
 * For information on usage and redistribution, and for a DISCLAIMER OF ALL
 * WARRANTIES, see the file, "LICENSE.txt," in this distribution.  */

/* LATER generic handling of reentrant output request and self-invoked set */

#include <string.h>
#include "m_pd.h"
#include "grow.h"

/* Prior to this call a caller is supposed to check for *nrequested > *sizep.
   Returns a reallocated buffer's pointer (success) or a given 'bufini'
   default value (failure).
   Upon return *nrequested contains the actual number of elements:
   requested (success) or a given default value of 'inisize' (failure). */
void *grow_nodata(int *nrequested, int *sizep, void *bufp,
		  int inisize, void *bufini, size_t typesize)
{
    int newsize = *sizep * 2;
    while (newsize < *nrequested) newsize *= 2;
    if (bufp == bufini)
	bufp = getbytes(newsize * typesize);
    else
	bufp = resizebytes(bufp, *sizep * typesize, newsize * typesize);
    if (bufp)
    {
	*sizep = newsize;
	return (bufp);
    }
    else
    {
	*nrequested = *sizep = inisize;
	return (bufini);
    }
}

/* Like grow_nodata(), but preserving first *nexisting elements. */
void *grow_withdata(int *nrequested, int *nexisting,
		    int *sizep, void *bufp,
		    int inisize, void *bufini, size_t typesize)
{
    int newsize = *sizep * 2;
    while (newsize < *nrequested) newsize *= 2;
    if (bufp == bufini)
    {
	if (!(bufp = getbytes(newsize * typesize)))
	{
	    *nrequested = *sizep = inisize;
	    return (bufini);
	}
	*sizep = newsize;
	memcpy(bufp, bufini, *nexisting * typesize);
    }
    else
    {
	int oldsize = *sizep;
	if (!(bufp = resizebytes(bufp, *sizep * typesize, newsize * typesize)))
	{
	    *nrequested = *sizep = inisize;
	    *nexisting = 0;
	    return (bufini);
	}
	*sizep = newsize;
    }
    return (bufp);
}

/* Like grow_nodata(), but preserving a 'tail' of *nexisting elements,
   starting from *startp. */
/* LATER rethink handling of a start pointer (clumsy now) */
void *grow_withtail(int *nrequested, int *nexisting, char **startp,
		    int *sizep, void *bufp,
		    int inisize, void *bufini, size_t typesize)
{
    int newsize = *sizep * 2;
    while (newsize < *nrequested) newsize *= 2;
    if (bufp == bufini)
    {
	char *oldstart = *startp;
	if (!(bufp = getbytes(newsize * typesize)))
	{
	    *nrequested = *sizep = inisize;
	    return (bufini);
	}
	*startp = (char *)bufp + (newsize - *nexisting) * typesize;
	*sizep = newsize;
	memcpy(*startp, oldstart, *nexisting * typesize);
    }
    else
    {
	int oldsize = *sizep;
	if (!(bufp = resizebytes(bufp, *sizep * typesize, newsize * typesize)))
	{
	    *startp = (char *)bufini + inisize * typesize;
	    *nrequested = *sizep = inisize;
	    *nexisting = 0;
	    return (bufini);
	}
	*startp = (char *)bufp + (newsize - *nexisting) * typesize;
	*sizep = newsize;
	memmove(*startp, (char *)bufp + (oldsize - *nexisting) * typesize,
		*nexisting * typesize);
    }
    return (bufp);
}
