2 * util.c - utility functions
6 * Copyright 2012-2023 Eric Biggers
8 * This file is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU Lesser General Public License as published by the Free
10 * Software Foundation; either version 3 of the License, or (at your option) any
13 * This file is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this file; if not, see https://www.gnu.org/licenses/.
32 #ifdef HAVE_SYS_SYSCTL_H
33 # include <sys/types.h>
34 # include <sys/sysctl.h>
36 #ifdef HAVE_SYS_SYSCALL_H
37 # include <sys/syscall.h>
42 #include "wimlib/assert.h"
43 #include "wimlib/error.h"
44 #include "wimlib/timestamp.h"
45 #include "wimlib/util.h"
51 static void *(*wimlib_malloc_func) (size_t) = malloc;
52 static void (*wimlib_free_func) (void *) = free;
53 static void *(*wimlib_realloc_func)(void *, size_t) = realloc;
56 wimlib_malloc(size_t size)
61 ptr = (*wimlib_malloc_func)(size);
72 wimlib_free_memory(void *ptr)
74 (*wimlib_free_func)(ptr);
78 wimlib_realloc(void *ptr, size_t size)
82 return (*wimlib_realloc_func)(ptr, size);
86 wimlib_calloc(size_t nmemb, size_t size)
88 size_t total_size = nmemb * size;
91 if (size != 0 && nmemb > SIZE_MAX / size) {
96 p = MALLOC(total_size);
98 p = memset(p, 0, total_size);
103 wimlib_strdup(const char *str)
105 return memdup(str, strlen(str) + 1);
110 wimlib_wcsdup(const wchar_t *str)
112 return memdup(str, (wcslen(str) + 1) * sizeof(wchar_t));
117 wimlib_aligned_malloc(size_t size, size_t alignment)
119 wimlib_assert(is_power_of_2(alignment));
121 void *ptr = MALLOC(sizeof(void *) + alignment - 1 + size);
123 void *orig_ptr = ptr;
124 ptr = (void *)ALIGN((uintptr_t)ptr + sizeof(void *), alignment);
125 ((void **)ptr)[-1] = orig_ptr;
131 wimlib_aligned_free(void *ptr)
134 FREE(((void **)ptr)[-1]);
138 memdup(const void *mem, size_t size)
140 void *ptr = MALLOC(size);
142 ptr = memcpy(ptr, mem, size);
146 /* API function documented in wimlib.h */
148 wimlib_set_memory_allocator(void *(*malloc_func)(size_t),
149 void (*free_func)(void *),
150 void *(*realloc_func)(void *, size_t))
152 wimlib_malloc_func = malloc_func ? malloc_func : malloc;
153 wimlib_free_func = free_func ? free_func : free;
154 wimlib_realloc_func = realloc_func ? realloc_func : realloc;
163 void *mempcpy(void *dst, const void *src, size_t n)
165 return memcpy(dst, src, n) + n;
169 /**************************
170 * Random number generation
171 **************************/
175 * Generate @n cryptographically secure random bytes (thread-safe)
177 * This is the UNIX version. It uses the Linux getrandom() system call if
178 * available; otherwise, it falls back to reading from /dev/urandom.
181 get_random_bytes(void *p, size_t n)
185 #ifdef __NR_getrandom
186 static bool getrandom_unavailable;
188 if (getrandom_unavailable)
189 goto try_dev_urandom;
191 int res = syscall(__NR_getrandom, p, n, 0);
192 if (unlikely(res < 0)) {
193 if (errno == ENOSYS) {
194 getrandom_unavailable = true;
195 goto try_dev_urandom;
199 ERROR_WITH_ERRNO("getrandom() failed");
210 #endif /* __NR_getrandom */
211 int fd = open("/dev/urandom", O_RDONLY);
213 ERROR_WITH_ERRNO("Unable to open /dev/urandom");
217 int res = read(fd, p, min(n, INT_MAX));
218 if (unlikely(res < 0)) {
221 ERROR_WITH_ERRNO("Error reading from /dev/urandom");
233 * Generate @n cryptographically secure random alphanumeric characters
236 * This is implemented on top of get_random_bytes(). For efficiency the calls
237 * to get_random_bytes() are batched.
240 get_random_alnum_chars(tchar *p, size_t n)
246 for (; n != 0; p++, n--) {
249 if (r_idx >= r_end) {
251 r_end = min(n, ARRAY_LEN(r));
252 get_random_bytes(r, r_end * sizeof(r[0]));
255 STATIC_ASSERT(sizeof(r[0]) == sizeof(u32));
256 while (unlikely(r[r_idx] >= UINT32_MAX - (UINT32_MAX % 62)))
257 get_random_bytes(&r[r_idx], sizeof(r[0]));
270 /************************
272 ************************/
276 get_available_cpus(void)
278 long n = sysconf(_SC_NPROCESSORS_ONLN);
279 if (n < 1 || n >= UINT_MAX) {
280 WARNING("Failed to determine number of processors; assuming 1.");
289 get_available_memory(void)
291 #if defined(_SC_PAGESIZE) && defined(_SC_PHYS_PAGES)
292 long page_size = sysconf(_SC_PAGESIZE);
293 long num_pages = sysconf(_SC_PHYS_PAGES);
294 if (page_size <= 0 || num_pages <= 0)
296 return ((u64)page_size * (u64)num_pages);
298 int mib[2] = {CTL_HW, HW_MEMSIZE};
300 size_t len = sizeof(memsize);
301 if (sysctl(mib, ARRAY_LEN(mib), &memsize, &len, NULL, 0) < 0 || len != 8)
307 WARNING("Failed to determine available memory; assuming 1 GiB");