/* GLIB - Library of useful routines for C programming * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ /* * Modified by the GLib Team and others 1997-2000. See the AUTHORS * file for a list of people on the GLib Team. See the ChangeLog * files for a list of changes. These files are distributed with * GLib at ftp://ftp.gtk.org/pub/gtk/. */ /* * MT safe */ #include "config.h" #include "gmem.h" #if defined(HAVE_POSIX_MEMALIGN) && !defined(_XOPEN_SOURCE) # define _XOPEN_SOURCE 600 #endif #if defined(HAVE_MEMALIGN) || defined(HAVE__ALIGNED_MALLOC) /* Required for _aligned_malloc() and _aligned_free() on Windows */ #include #endif #ifdef HAVE__ALIGNED_MALLOC /* _aligned_malloc() takes parameters of aligned_malloc() in reverse order */ # define aligned_alloc(alignment, size) _aligned_malloc (size, alignment) /* _aligned_malloc()'ed memory must be freed by _align_free() on MSVC */ # define aligned_free(x) _aligned_free (x) #else # define aligned_free(x) free (x) #endif #include #include #include #include "gslice.h" #include "gbacktrace.h" #include "gtestutils.h" #include "gthread.h" #include "glib_trace.h" /* notes on macros: * having G_DISABLE_CHECKS defined disables use of glib_mem_profiler_table and * g_mem_profile(). * If g_mem_gc_friendly is TRUE, freed memory should be 0-wiped. */ /* --- variables --- */ static GMemVTable glib_mem_vtable = { malloc, realloc, free, calloc, malloc, realloc, }; /* --- functions --- */ /** * g_malloc: * @n_bytes: the number of bytes to allocate * * Allocates @n_bytes bytes of memory. * If @n_bytes is 0 it returns %NULL. * * If the allocation fails (because the system is out of memory), * the program is terminated. * * Returns: a pointer to the allocated memory */ gpointer g_malloc (gsize n_bytes) { if (G_LIKELY (n_bytes)) { gpointer mem; mem = malloc (n_bytes); TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 0, 0)); if (mem) return mem; g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes", G_STRLOC, n_bytes); } TRACE(GLIB_MEM_ALLOC((void*) NULL, (int) n_bytes, 0, 0)); return NULL; } /** * g_malloc0: * @n_bytes: the number of bytes to allocate * * Allocates @n_bytes bytes of memory, initialized to 0's. * If @n_bytes is 0 it returns %NULL. * * If the allocation fails (because the system is out of memory), * the program is terminated. * * Returns: a pointer to the allocated memory */ gpointer g_malloc0 (gsize n_bytes) { if (G_LIKELY (n_bytes)) { gpointer mem; mem = calloc (1, n_bytes); TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 1, 0)); if (mem) return mem; g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes", G_STRLOC, n_bytes); } TRACE(GLIB_MEM_ALLOC((void*) NULL, (int) n_bytes, 1, 0)); return NULL; } /** * g_realloc: * @mem: (nullable): the memory to reallocate * @n_bytes: new size of the memory in bytes * * Reallocates the memory pointed to by @mem, so that it now has space for * @n_bytes bytes of memory. It returns the new address of the memory, which may * have been moved. @mem may be %NULL, in which case it's considered to * have zero-length. @n_bytes may be 0, in which case %NULL will be returned * and @mem will be freed unless it is %NULL. * * If the allocation fails (because the system is out of memory), * the program is terminated. * * Returns: the new address of the allocated memory */ gpointer g_realloc (gpointer mem, gsize n_bytes) { gpointer newmem; if (G_LIKELY (n_bytes)) { newmem = realloc (mem, n_bytes); TRACE (GLIB_MEM_REALLOC((void*) newmem, (void*)mem, (unsigned int) n_bytes, 0)); if (newmem) return newmem; g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes", G_STRLOC, n_bytes); } free (mem); TRACE (GLIB_MEM_REALLOC((void*) NULL, (void*)mem, 0, 0)); return NULL; } /** * g_free: * @mem: (nullable): the memory to free * * Frees the memory pointed to by @mem. * * If you know the allocated size of @mem, calling g_free_sized() may be faster, * depending on the libc implementation in use. * * Starting from GLib 2.78, this may happen automatically in case a GCC * compatible compiler is used with some optimization level and the allocated * size is known at compile time (see [documentation of * `__builtin_object_size()`](https://gcc.gnu.org/onlinedocs/gcc/Object-Size-Checking.html) * to understand its caveats). * * If @mem is %NULL it simply returns, so there is no need to check @mem * against %NULL before calling this function. */ void (g_free) (gpointer mem) { free (mem); TRACE(GLIB_MEM_FREE((void*) mem)); } /** * g_free_sized: * @mem: (nullable): the memory to free * @size: size of @mem, in bytes * * Frees the memory pointed to by @mem, assuming it is has the given @size. * * If @mem is %NULL this is a no-op (and @size is ignored). * * It is an error if @size doesn’t match the size passed when @mem was * allocated. @size is passed to this function to allow optimizations in the * allocator. If you don’t know the allocation size, use g_free() instead. * * In case a GCC compatible compiler is used, this function may be used * automatically via g_free() if the allocated size is known at compile time, * since GLib 2.78. * * Since: 2.76 */ void g_free_sized (void *mem, size_t size) { #ifdef HAVE_FREE_SIZED free_sized (mem, size); #else free (mem); #endif TRACE (GLIB_MEM_FREE ((void*) mem)); } /** * g_clear_pointer: (skip) * @pp: (nullable) (not optional) (inout) (transfer full): a pointer to a * variable, struct member etc. holding a pointer * @destroy: a function to which a gpointer can be passed, to destroy `*pp` * * Clears a reference to a variable. * * @pp must not be %NULL. * * If the reference is %NULL then this function does nothing. * Otherwise, the variable is destroyed using @destroy and the * pointer is set to %NULL. * * A macro is also included that allows this function to be used without * pointer casts. This will mask any warnings about incompatible function types * or calling conventions, so you must ensure that your @destroy function is * compatible with being called as [callback@GLib.DestroyNotify] using the * standard calling convention for the platform that GLib was compiled for; * otherwise the program will experience undefined behaviour. * * Examples of this kind of undefined behaviour include using many Windows Win32 * APIs, as well as many if not all OpenGL and Vulkan calls on 32-bit Windows, * which typically use the `__stdcall` calling convention rather than the * `__cdecl` calling convention. * * The affected functions can be used by wrapping them in a * [callback@GLib.DestroyNotify] that is declared with the standard calling * convention: * * ```c * // Wrapper needed to avoid mismatched calling conventions on Windows * static void * destroy_sync (void *sync) * { * glDeleteSync (sync); * } * * // … * * g_clear_pointer (&sync, destroy_sync); * ``` * * Since: 2.34 **/ #undef g_clear_pointer void g_clear_pointer (gpointer *pp, GDestroyNotify destroy) { gpointer _p; _p = *pp; if (_p) { *pp = NULL; destroy (_p); } } /** * g_try_malloc: * @n_bytes: number of bytes to allocate. * * Attempts to allocate @n_bytes, and returns %NULL on failure. * Contrast with g_malloc(), which aborts the program on failure. * * Returns: the allocated memory, or %NULL. */ gpointer g_try_malloc (gsize n_bytes) { gpointer mem; if (G_LIKELY (n_bytes)) mem = malloc (n_bytes); else mem = NULL; TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 0, 1)); return mem; } /** * g_try_malloc0: * @n_bytes: number of bytes to allocate * * Attempts to allocate @n_bytes, initialized to 0's, and returns %NULL on * failure. Contrast with g_malloc0(), which aborts the program on failure. * * Since: 2.8 * Returns: the allocated memory, or %NULL */ gpointer g_try_malloc0 (gsize n_bytes) { gpointer mem; if (G_LIKELY (n_bytes)) mem = calloc (1, n_bytes); else mem = NULL; return mem; } /** * g_try_realloc: * @mem: (nullable): previously-allocated memory, or %NULL. * @n_bytes: number of bytes to allocate. * * Attempts to realloc @mem to a new size, @n_bytes, and returns %NULL * on failure. Contrast with g_realloc(), which aborts the program * on failure. * * If @mem is %NULL, behaves the same as g_try_malloc(). * * Returns: the allocated memory, or %NULL. */ gpointer g_try_realloc (gpointer mem, gsize n_bytes) { gpointer newmem; if (G_LIKELY (n_bytes)) newmem = realloc (mem, n_bytes); else { newmem = NULL; free (mem); } TRACE (GLIB_MEM_REALLOC((void*) newmem, (void*)mem, (unsigned int) n_bytes, 1)); return newmem; } #define SIZE_OVERFLOWS(a,b) (G_UNLIKELY ((b) > 0 && (a) > G_MAXSIZE / (b))) /** * g_malloc_n: * @n_blocks: the number of blocks to allocate * @n_block_bytes: the size of each block in bytes * * This function is similar to g_malloc(), allocating (@n_blocks * @n_block_bytes) bytes, * but care is taken to detect possible overflow during multiplication. * * If the allocation fails (because the system is out of memory), * the program is terminated. * * Since: 2.24 * Returns: a pointer to the allocated memory */ gpointer g_malloc_n (gsize n_blocks, gsize n_block_bytes) { if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) { g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes", G_STRLOC, n_blocks, n_block_bytes); } return g_malloc (n_blocks * n_block_bytes); } /** * g_malloc0_n: * @n_blocks: the number of blocks to allocate * @n_block_bytes: the size of each block in bytes * * This function is similar to g_malloc0(), allocating (@n_blocks * @n_block_bytes) bytes, * but care is taken to detect possible overflow during multiplication. * * If the allocation fails (because the system is out of memory), * the program is terminated. * * Since: 2.24 * Returns: a pointer to the allocated memory */ gpointer g_malloc0_n (gsize n_blocks, gsize n_block_bytes) { if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) { g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes", G_STRLOC, n_blocks, n_block_bytes); } return g_malloc0 (n_blocks * n_block_bytes); } /** * g_realloc_n: * @mem: (nullable): the memory to reallocate * @n_blocks: the number of blocks to allocate * @n_block_bytes: the size of each block in bytes * * This function is similar to g_realloc(), allocating (@n_blocks * @n_block_bytes) bytes, * but care is taken to detect possible overflow during multiplication. * * If the allocation fails (because the system is out of memory), * the program is terminated. * * Since: 2.24 * Returns: the new address of the allocated memory */ gpointer g_realloc_n (gpointer mem, gsize n_blocks, gsize n_block_bytes) { if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) { g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes", G_STRLOC, n_blocks, n_block_bytes); } return g_realloc (mem, n_blocks * n_block_bytes); } /** * g_try_malloc_n: * @n_blocks: the number of blocks to allocate * @n_block_bytes: the size of each block in bytes * * This function is similar to g_try_malloc(), allocating (@n_blocks * @n_block_bytes) bytes, * but care is taken to detect possible overflow during multiplication. * * Since: 2.24 * Returns: the allocated memory, or %NULL. */ gpointer g_try_malloc_n (gsize n_blocks, gsize n_block_bytes) { if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) return NULL; return g_try_malloc (n_blocks * n_block_bytes); } /** * g_try_malloc0_n: * @n_blocks: the number of blocks to allocate * @n_block_bytes: the size of each block in bytes * * This function is similar to g_try_malloc0(), allocating (@n_blocks * @n_block_bytes) bytes, * but care is taken to detect possible overflow during multiplication. * * Since: 2.24 * Returns: the allocated memory, or %NULL */ gpointer g_try_malloc0_n (gsize n_blocks, gsize n_block_bytes) { if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) return NULL; return g_try_malloc0 (n_blocks * n_block_bytes); } /** * g_try_realloc_n: * @mem: (nullable): previously-allocated memory, or %NULL. * @n_blocks: the number of blocks to allocate * @n_block_bytes: the size of each block in bytes * * This function is similar to g_try_realloc(), allocating (@n_blocks * @n_block_bytes) bytes, * but care is taken to detect possible overflow during multiplication. * * Since: 2.24 * Returns: the allocated memory, or %NULL. */ gpointer g_try_realloc_n (gpointer mem, gsize n_blocks, gsize n_block_bytes) { if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) return NULL; return g_try_realloc (mem, n_blocks * n_block_bytes); } /** * g_mem_is_system_malloc: * * Checks whether the allocator used by g_malloc() is the system's * malloc implementation. If it returns %TRUE memory allocated with * malloc() can be used interchangeably with memory allocated using g_malloc(). * This function is useful for avoiding an extra copy of allocated memory returned * by a non-GLib-based API. * * Returns: if %TRUE, malloc() and g_malloc() can be mixed. * * Deprecated: 2.46: GLib always uses the system malloc, so this function always * returns %TRUE. **/ gboolean g_mem_is_system_malloc (void) { return TRUE; } /** * g_mem_set_vtable: * @vtable: table of memory allocation routines. * * This function used to let you override the memory allocation function. * However, its use was incompatible with the use of global constructors * in GLib and GIO, because those use the GLib allocators before main is * reached. Therefore this function is now deprecated and is just a stub. * * Deprecated: 2.46: This function now does nothing. Use other memory * profiling tools instead */ void g_mem_set_vtable (GMemVTable *vtable) { g_warning (G_STRLOC ": custom memory allocation vtable not supported"); } /** * glib_mem_profiler_table: * * Used to be a #GMemVTable containing profiling variants of the memory * allocation functions, but this variable shouldn't be modified anymore. * * Deprecated: 2.46: Use other memory profiling tools instead */ GMemVTable *glib_mem_profiler_table = &glib_mem_vtable; /** * g_mem_profile: * * GLib used to support some tools for memory profiling, but this * no longer works. There are many other useful tools for memory * profiling these days which can be used instead. * * Deprecated: 2.46: Use other memory profiling tools instead */ void g_mem_profile (void) { g_warning (G_STRLOC ": memory profiling not supported"); } /** * g_aligned_alloc: * @n_blocks: the number of blocks to allocate * @n_block_bytes: the size of each block in bytes * @alignment: the alignment to be enforced, which must be a positive power of 2 * and a multiple of `sizeof(void*)` * * This function is similar to g_malloc(), allocating (@n_blocks * @n_block_bytes) * bytes, but care is taken to align the allocated memory to with the given * alignment value. Additionally, it will detect possible overflow during * multiplication. * * If the allocation fails (because the system is out of memory), * the program is terminated. * * Aligned memory allocations returned by this function can only be * freed using g_aligned_free_sized() or g_aligned_free(). * * Returns: (transfer full): the allocated memory * * Since: 2.72 */ gpointer g_aligned_alloc (gsize n_blocks, gsize n_block_bytes, gsize alignment) { gpointer res = NULL; gsize real_size; if (G_UNLIKELY ((alignment == 0) || (alignment & (alignment - 1)) != 0)) { g_error ("%s: alignment %"G_GSIZE_FORMAT" must be a positive power of two", G_STRLOC, alignment); } if (G_UNLIKELY ((alignment % sizeof (void *)) != 0)) { g_error ("%s: alignment %"G_GSIZE_FORMAT" must be a multiple of %"G_GSIZE_FORMAT, G_STRLOC, alignment, sizeof (void *)); } if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) { g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes", G_STRLOC, n_blocks, n_block_bytes); } real_size = n_blocks * n_block_bytes; if (G_UNLIKELY (real_size == 0)) { TRACE(GLIB_MEM_ALLOC((void*) NULL, (int) real_size, 0, 0)); return NULL; } /* We need to clear errno because posix_memalign() will use its return * value in the same way memalign() and aligned_alloc() will set errno. * Additionally, posix_memalign() will warn if its return value is left * unassigned. * * We handle all possible return values (ENOMEM and EINVAL) with either * precondition or postcondition checking. */ errno = 0; #if defined(HAVE_POSIX_MEMALIGN) errno = posix_memalign (&res, alignment, real_size); #elif defined(HAVE_ALIGNED_ALLOC) || defined(HAVE__ALIGNED_MALLOC) /* real_size must be a multiple of alignment */ if (real_size % alignment != 0) { gsize offset = real_size % alignment; if (G_MAXSIZE - real_size < (alignment - offset)) { g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"+%"G_GSIZE_FORMAT" bytes", G_STRLOC, real_size, (alignment - offset)); } real_size += (alignment - offset); } res = aligned_alloc (alignment, real_size); #elif defined(HAVE_MEMALIGN) res = memalign (alignment, real_size); #else # error "This platform does not have an aligned memory allocator." #endif TRACE (GLIB_MEM_ALLOC((void*) res, (unsigned int) real_size, 0, 0)); if (res) return res; g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes", G_STRLOC, real_size); return NULL; } /** * g_aligned_alloc0: * @n_blocks: the number of blocks to allocate * @n_block_bytes: the size of each block in bytes * @alignment: the alignment to be enforced, which must be a positive power of 2 * and a multiple of `sizeof(void*)` * * This function is similar to g_aligned_alloc(), but it will * also clear the allocated memory before returning it. * * Returns: (transfer full): the allocated, cleared memory * * Since: 2.72 */ gpointer g_aligned_alloc0 (gsize n_blocks, gsize n_block_bytes, gsize alignment) { gpointer res = g_aligned_alloc (n_blocks, n_block_bytes, alignment); if (G_LIKELY (res != NULL)) memset (res, 0, n_blocks * n_block_bytes); return res; } /** * g_aligned_free: * @mem: (nullable): the memory to deallocate * * Frees the memory allocated by g_aligned_alloc(). * * Since: 2.72 */ void g_aligned_free (gpointer mem) { aligned_free (mem); } /** * g_aligned_free_sized: * @mem: (nullable): the memory to free * @alignment: alignment of @mem * @size: size of @mem, in bytes * * Frees the memory pointed to by @mem, assuming it is has the given @size and * @alignment. * * If @mem is %NULL this is a no-op (and @size is ignored). * * It is an error if @size doesn’t match the size, or @alignment doesn’t match * the alignment, passed when @mem was allocated. @size and @alignment are * passed to this function to allow optimizations in the allocator. If you * don’t know either of them, use g_aligned_free() instead. * * Since: 2.76 */ void g_aligned_free_sized (void *mem, size_t alignment, size_t size) { #ifdef HAVE_FREE_ALIGNED_SIZED free_aligned_sized (mem, alignment, size); #else aligned_free (mem); #endif }