1*2cb7cdfcSclaudio /* $OpenBSD: recallocarray.c,v 1.2 2021/03/18 11:16:58 claudio Exp $ */
23f0eb563Sotto /*
33f0eb563Sotto * Copyright (c) 2008, 2017 Otto Moerbeek <otto@drijf.net>
43f0eb563Sotto *
53f0eb563Sotto * Permission to use, copy, modify, and distribute this software for any
63f0eb563Sotto * purpose with or without fee is hereby granted, provided that the above
73f0eb563Sotto * copyright notice and this permission notice appear in all copies.
83f0eb563Sotto *
93f0eb563Sotto * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
103f0eb563Sotto * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
113f0eb563Sotto * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
123f0eb563Sotto * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
133f0eb563Sotto * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
143f0eb563Sotto * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
153f0eb563Sotto * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
163f0eb563Sotto */
173f0eb563Sotto
183f0eb563Sotto #include <errno.h>
193f0eb563Sotto #include <stdlib.h>
203f0eb563Sotto #include <stdint.h>
213f0eb563Sotto #include <string.h>
223f0eb563Sotto #include <unistd.h>
233f0eb563Sotto
243f0eb563Sotto /*
253f0eb563Sotto * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
263f0eb563Sotto * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
273f0eb563Sotto */
283f0eb563Sotto #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
293f0eb563Sotto
303f0eb563Sotto void *
recallocarray(void * ptr,size_t oldnmemb,size_t newnmemb,size_t size)313f0eb563Sotto recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size)
323f0eb563Sotto {
333f0eb563Sotto size_t oldsize, newsize;
343f0eb563Sotto void *newptr;
353f0eb563Sotto
363f0eb563Sotto if (ptr == NULL)
373f0eb563Sotto return calloc(newnmemb, size);
383f0eb563Sotto
393f0eb563Sotto if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
403f0eb563Sotto newnmemb > 0 && SIZE_MAX / newnmemb < size) {
413f0eb563Sotto errno = ENOMEM;
423f0eb563Sotto return NULL;
433f0eb563Sotto }
443f0eb563Sotto newsize = newnmemb * size;
453f0eb563Sotto
463f0eb563Sotto if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
473f0eb563Sotto oldnmemb > 0 && SIZE_MAX / oldnmemb < size) {
483f0eb563Sotto errno = EINVAL;
493f0eb563Sotto return NULL;
503f0eb563Sotto }
513f0eb563Sotto oldsize = oldnmemb * size;
523f0eb563Sotto
533f0eb563Sotto /*
543f0eb563Sotto * Don't bother too much if we're shrinking just a bit,
553f0eb563Sotto * we do not shrink for series of small steps, oh well.
563f0eb563Sotto */
573f0eb563Sotto if (newsize <= oldsize) {
583f0eb563Sotto size_t d = oldsize - newsize;
593f0eb563Sotto
60*2cb7cdfcSclaudio if (d < oldsize / 2 && d < (size_t)getpagesize()) {
613f0eb563Sotto memset((char *)ptr + newsize, 0, d);
623f0eb563Sotto return ptr;
633f0eb563Sotto }
643f0eb563Sotto }
653f0eb563Sotto
663f0eb563Sotto newptr = malloc(newsize);
673f0eb563Sotto if (newptr == NULL)
683f0eb563Sotto return NULL;
693f0eb563Sotto
703f0eb563Sotto if (newsize > oldsize) {
713f0eb563Sotto memcpy(newptr, ptr, oldsize);
723f0eb563Sotto memset((char *)newptr + oldsize, 0, newsize - oldsize);
733f0eb563Sotto } else
743f0eb563Sotto memcpy(newptr, ptr, newsize);
753f0eb563Sotto
763f0eb563Sotto explicit_bzero(ptr, oldsize);
773f0eb563Sotto free(ptr);
783f0eb563Sotto
793f0eb563Sotto return newptr;
803f0eb563Sotto }
813f0eb563Sotto DEF_WEAK(recallocarray);
82