Home | History | Annotate | Line # | Download | only in compat
recallocarray.c revision 1.1.1.1.2.2
      1 /*	$OpenBSD: recallocarray.c,v 1.1 2017/03/06 18:44:21 otto Exp $	*/
      2 /*
      3  * Copyright (c) 2008, 2017 Otto Moerbeek <otto (at) drijf.net>
      4  *
      5  * Permission to use, copy, modify, and distribute this software for any
      6  * purpose with or without fee is hereby granted, provided that the above
      7  * copyright notice and this permission notice appear in all copies.
      8  *
      9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     16  */
     17 
     18 #include <errno.h>
     19 #include <stdlib.h>
     20 #include <stdint.h>
     21 #include <string.h>
     22 #include <unistd.h>
     23 
     24 #include "compat.h"
     25 
     26 /*
     27  * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
     28  * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
     29  */
     30 #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
     31 
     32 void *
     33 recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size)
     34 {
     35 	size_t oldsize, newsize;
     36 	void *newptr;
     37 
     38 	if (ptr == NULL)
     39 		return calloc(newnmemb, size);
     40 
     41 	if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
     42 	    newnmemb > 0 && SIZE_MAX / newnmemb < size) {
     43 		errno = ENOMEM;
     44 		return NULL;
     45 	}
     46 	newsize = newnmemb * size;
     47 
     48 	if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
     49 	    oldnmemb > 0 && SIZE_MAX / oldnmemb < size) {
     50 		errno = EINVAL;
     51 		return NULL;
     52 	}
     53 	oldsize = oldnmemb * size;
     54 
     55 	/*
     56 	 * Don't bother too much if we're shrinking just a bit,
     57 	 * we do not shrink for series of small steps, oh well.
     58 	 */
     59 	if (newsize <= oldsize) {
     60 		size_t d = oldsize - newsize;
     61 
     62 		if (d < oldsize / 2 && d < (size_t)getpagesize()) {
     63 			memset((char *)ptr + newsize, 0, d);
     64 			return ptr;
     65 		}
     66 	}
     67 
     68 	newptr = malloc(newsize);
     69 	if (newptr == NULL)
     70 		return NULL;
     71 
     72 	if (newsize > oldsize) {
     73 		memcpy(newptr, ptr, oldsize);
     74 		memset((char *)newptr + oldsize, 0, newsize - oldsize);
     75 	} else
     76 		memcpy(newptr, ptr, newsize);
     77 
     78 	explicit_bzero(ptr, oldsize);
     79 	free(ptr);
     80 
     81 	return newptr;
     82 }
     83