reintroduce calloc elison of memset for direct-mmapped allocations
[oweals/musl.git] / src / malloc / calloc.c
1 #include <stdlib.h>
2 #include <stdint.h>
3 #include <string.h>
4 #include <errno.h>
5 #include "dynlink.h"
6
7 static size_t mal0_clear(char *p, size_t n)
8 {
9         const size_t pagesz = 4096; /* arbitrary */
10         if (n < pagesz) return n;
11 #ifdef __GNUC__
12         typedef uint64_t __attribute__((__may_alias__)) T;
13 #else
14         typedef unsigned char T;
15 #endif
16         char *pp = p + n;
17         size_t i = (uintptr_t)pp & (pagesz - 1);
18         for (;;) {
19                 pp = memset(pp - i, 0, i);
20                 if (pp - p < pagesz) return pp - p;
21                 for (i = pagesz; i; i -= 2*sizeof(T), pp -= 2*sizeof(T))
22                         if (((T *)pp)[-1] | ((T *)pp)[-2])
23                                 break;
24         }
25 }
26
27 static int allzerop(void *p)
28 {
29         return 0;
30 }
31 weak_alias(allzerop, __malloc_allzerop);
32
33 void *calloc(size_t m, size_t n)
34 {
35         if (n && m > (size_t)-1/n) {
36                 errno = ENOMEM;
37                 return 0;
38         }
39         n *= m;
40         void *p = malloc(n);
41         if (!p || (!__malloc_replaced && __malloc_allzerop(p)))
42                 return p;
43         n = mal0_clear(p, n);
44         return memset(p, 0, n);
45 }