| /* libnih |
| * |
| * alloc.c - multi-reference hierarchial allocator |
| * |
| * Copyright © 2009 Scott James Remnant <scott@netsplit.com>. |
| * Copyright © 2009 Canonical Ltd. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining |
| * a copy of this software and associated documentation files (the |
| * "Software"), to deal in the Software without restriction, including |
| * without limitation the rights to use, copy, modify, merge, publish, |
| * distribute, sublicense, and/or sell copies of the Software, and to |
| * permit persons to whom the Software is furnished to do so, subject to |
| * the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR |
| * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF |
| * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| # include <config.h> |
| #endif /* HAVE_CONFIG_H */ |
| |
| |
| #include <malloc.h> |
| #include <stdlib.h> |
| |
| #include <nih/macros.h> |
| #include <nih/logging.h> |
| #include <nih/list.h> |
| |
| #include "alloc.h" |
| |
| |
| /** |
| * NihAllocCtx: |
| * @parents: parents of this context, |
| * @children: children of this context, |
| * @destructor: function to be called when freed. |
| * |
| * This structure is placed before all allocations in memory and is used |
| * to build up an n-ary tree of them. Allocations may have multiple |
| * parent references and multiple children. Allocations are automatically |
| * freed if the last parent reference is freed. When an allocation is |
| * freed, all children are unreferenced and any destructors called. |
| * |
| * Members of @parents and @children are both NihAllocRef objects. |
| **/ |
| typedef struct nih_alloc_ctx { |
| NihList parents; |
| NihList children; |
| NihDestructor destructor; |
| } NihAllocCtx; |
| |
| /** |
| * NihAllocRef: |
| * @children_entry: list head in parent's children list, |
| * @parents_entry: list head in child's parents list, |
| * @parent: pointer to parent context, |
| * @child: pointer to child context. |
| * |
| * This structure is shared by both @parent and @child denoting a reference |
| * between the two of them. It is placed in @parent's children list through |
| * @children_entry and @child's parents list through @parents_entry. |
| **/ |
| typedef struct nih_alloc_ref { |
| NihList children_entry; |
| NihList parents_entry; |
| NihAllocCtx *parent; |
| NihAllocCtx *child; |
| } NihAllocRef; |
| |
| |
| /** |
| * NIH_ALLOC_CTX: |
| * @ptr: pointer to block of memory. |
| * |
| * Obtain the location of the NihAllocCtx structure given a pointer to the |
| * block of memory beyond it. |
| * |
| * Returns: pointer to NihAllocCtx structure. |
| **/ |
| #define NIH_ALLOC_CTX(ptr) ((NihAllocCtx *)(ptr) - 1) |
| |
| /** |
| * NIH_ALLOC_PTR: |
| * @ctx: pointer to NihAllocCtx structure. |
| * |
| * Obtain the location of the block of memory given a pointer to the |
| * NihAllocCtx structure in front of it. |
| * |
| * Returns: pointer to block of memory. |
| **/ |
| #define NIH_ALLOC_PTR(ctx) ((void *)((NihAllocCtx *)(ctx) + 1)) |
| |
| /** |
| * NIH_ALLOC_FINALISED: |
| * |
| * Flag placed in the destructor field of a context to indicate the |
| * destructor has been called and the object is pending being freed. |
| **/ |
| #define NIH_ALLOC_FINALISED ((void *)-1) |
| |
| |
| /* Prototypes for static functions */ |
| static inline int nih_alloc_context_free (NihAllocCtx *ctx); |
| |
| static inline NihAllocRef *nih_alloc_ref_new (NihAllocCtx *parent, |
| NihAllocCtx *child) |
| __attribute__ ((malloc)); |
| static inline void nih_alloc_ref_free (NihAllocRef *ref); |
| static inline NihAllocRef *nih_alloc_ref_lookup (NihAllocCtx *parent, |
| NihAllocCtx *child); |
| |
| |
| /* Point to the functions we actually call for allocation. */ |
| void *(*__nih_malloc) (size_t size) = malloc; |
| void *(*__nih_realloc) (void *ptr, size_t size) = realloc; |
| void (*__nih_free) (void *ptr) = free; |
| |
| |
| /** |
| * nih_alloc: |
| * @parent: parent object for new object, |
| * @size: size of requested object. |
| * |
| * Allocates an object in memory of at least @size bytes and returns a |
| * pointer to it. |
| * |
| * If @parent is not NULL, it should be a pointer to another object which |
| * will be used as a parent for the returned object. When all parents |
| * of the returned object are freed, the returned object will also be |
| * freed. |
| * |
| * If you have clean-up that you would like to run, you can assign a |
| * destructor using the nih_alloc_set_destructor() function. |
| * |
| * Returns: newly allocated object or NULL if insufficient memory. |
| **/ |
| void * |
| nih_alloc (const void *parent, |
| size_t size) |
| { |
| NihAllocCtx *ctx; |
| |
| ctx = __nih_malloc (sizeof (NihAllocCtx) + size); |
| if (! ctx) |
| return NULL; |
| |
| nih_list_init (&ctx->parents); |
| nih_list_init (&ctx->children); |
| |
| ctx->destructor = NULL; |
| |
| if (parent) |
| nih_alloc_ref_new (NIH_ALLOC_CTX (parent), ctx); |
| |
| return NIH_ALLOC_PTR (ctx); |
| } |
| |
| |
| /** |
| * nih_realloc: |
| * @ptr: object to reallocate, |
| * @parent: parent object of new object, |
| * @size: size of new object. |
| * |
| * Adjusts the size of the object @ptr to be at least @size bytes, which |
| * may be larger or smaller than the existing object, and returns the |
| * new pointer. |
| * |
| * If @ptr is NULL, this simply calls nih_alloc() and passes both @parent |
| * and @size to it, returning the returned object. |
| * |
| * If @ptr is not NULL, @parent is ignored; though it is usual to pass a |
| * parent of @ptr for style reasons. |
| * |
| * Returns: reallocated object or NULL if insufficient memory. |
| **/ |
| void * |
| nih_realloc (void * ptr, |
| const void *parent, |
| size_t size) |
| { |
| NihAllocCtx *ctx; |
| NihList * first_parent = NULL; |
| NihList * first_child = NULL; |
| |
| if (! ptr) |
| return nih_alloc (parent, size); |
| |
| ctx = NIH_ALLOC_CTX (ptr); |
| nih_assert (ctx->destructor != NIH_ALLOC_FINALISED); |
| |
| /* This is somewhat more difficult than alloc or free because we |
| * have two lists of pointers to worry about. Fortunately the |
| * properties of NihList help us a lot here. |
| * |
| * The problem is that references between us and our parents, |
| * and references between us and our children, all contain list |
| * pointers that are potentially invalid once relloc has been |
| * called. |
| * |
| * We could strip it all down before calling realloc then rebuild |
| * it afterwards, but that's expensive and could be error-prone in |
| * the case where the allocator fails. |
| * |
| * The solution is to rely on a property of nih_list_add(). The |
| * entry passed (to be added) is cut out of its containing list |
| * without dereferencing the return pointers, this means we can |
| * cut the bad pointers out simply by calling nih_list_add() |
| * to put the new entry back in the same position. |
| * |
| * Of course, this only works in the non-empty list case as trying |
| * to cut an entry out of an empty list would dereference those |
| * invalid pointers. Happily all we need to do for the empty |
| * list case is call nih_list_init() again. |
| * |
| * So we just remember the first parent and first child reference, |
| * or NULL if the list is empty. |
| */ |
| |
| if (! NIH_LIST_EMPTY (&ctx->parents)) |
| first_parent = ctx->parents.next; |
| if (! NIH_LIST_EMPTY (&ctx->children)) |
| first_child = ctx->children.next; |
| |
| /* Now do the actual realloc(), if this fails then we can just |
| * return NULL since we've not actually changed anything. |
| */ |
| ctx = __nih_realloc (ctx, sizeof (NihAllocCtx) + size); |
| if (! ctx) |
| return NULL; |
| |
| /* Now update our parents and children lists, or reinitialise, |
| * as noted above this ensures that all the pointers are correct |
| */ |
| if (first_parent) { |
| nih_list_add_after (first_parent, &ctx->parents); |
| } else { |
| nih_list_init (&ctx->parents); |
| } |
| |
| if (first_child) { |
| nih_list_add_after (first_child, &ctx->children); |
| } else { |
| nih_list_init (&ctx->children); |
| } |
| |
| /* We still have to fix up the parent and child pointers, but |
| * that's easy. |
| */ |
| NIH_LIST_FOREACH (&ctx->parents, iter) { |
| NihAllocRef *ref = NIH_LIST_ITER (iter, NihAllocRef, |
| parents_entry); |
| |
| ref->child = ctx; |
| } |
| |
| NIH_LIST_FOREACH (&ctx->children, iter) { |
| NihAllocRef *ref = NIH_LIST_ITER (iter, NihAllocRef, |
| children_entry); |
| |
| ref->parent = ctx; |
| } |
| |
| return NIH_ALLOC_PTR (ctx); |
| } |
| |
| |
| /** |
| * nih_free: |
| * @ptr: object to free. |
| * |
| * Returns the object @ptr to the allocator so the memory consumed may be |
| * re-used by something else. |
| * |
| * All parent references are discarded and the destructor for @ptr is called. |
| * Then all children are recursively unreferenced. Those that have no |
| * remaining parent references will also have their destructors called and |
| * their children unreferenced, etc. Once all destructors have been called, |
| * the objects themselves are freed. |
| * |
| * If you call nih_free() on an object with parent references, you should |
| * make sure that any pointers to the object are reset. If you are unsure |
| * whether or not there are references you should call nih_discard(), if |
| * you only want to discard a particular parent reference you should call |
| * nih_unref(). |
| * |
| * Returns: return value from @ptr's destructor, or 0. |
| **/ |
| int |
| nih_free (void *ptr) |
| { |
| NihAllocCtx *ctx; |
| |
| nih_assert (ptr != NULL); |
| |
| ctx = NIH_ALLOC_CTX (ptr); |
| nih_assert (ctx->destructor != NIH_ALLOC_FINALISED); |
| |
| /* Cast off our parents first, without recursing. This ensures |
| * we always have zero references before we call the destructor, |
| * and has the somewhat neat property of breaking any reference |
| * loops. |
| */ |
| NIH_LIST_FOREACH_SAFE (&ctx->parents, iter) { |
| NihAllocRef *ref = NIH_LIST_ITER (iter, NihAllocRef, |
| parents_entry); |
| |
| nih_alloc_ref_free (ref); |
| } |
| |
| return nih_alloc_context_free (ctx); |
| } |
| |
| /** |
| * nih_discard: |
| * @ptr: object to discard. |
| * |
| * If the object @ptr has no parent references, then returns it to the |
| * allocator so the memory consumed may be re-used by something else. |
| * |
| * If @ptr has parent references, this function does nothing and returns. |
| * |
| * You would use nih_discard() when you allocated @ptr without any parent |
| * but have passed it to functions that may have taken a reference to it |
| * in the meantime. Compare with nih_free() which acts even if there are |
| * parent references, and nih_unref() which only removes a single parent |
| * reference. |
| * |
| * Returns: return value from @ptr's destructor, or 0. |
| **/ |
| int |
| nih_discard (void *ptr) |
| { |
| NihAllocCtx *ctx; |
| |
| nih_assert (ptr != NULL); |
| |
| ctx = NIH_ALLOC_CTX (ptr); |
| nih_assert (ctx->destructor != NIH_ALLOC_FINALISED); |
| |
| if (NIH_LIST_EMPTY (&ctx->parents)) |
| return nih_alloc_context_free (ctx); |
| |
| return 0; |
| } |
| |
| /** |
| * _nih_discard_local: |
| * @ptr: address of local object to be discarded. |
| * |
| * This function should never be called directly, it is used as part of the |
| * implementation of nih_local and simply calls nih_discard() with the local |
| * variable itself. |
| **/ |
| void |
| _nih_discard_local (void *ptraddr) |
| { |
| /* Can't just take void ** as a parameter, since that will upset |
| * gcc typechecking, and we want to be able to be used on any |
| * pointer type. |
| */ |
| void **ptr = (void **)ptraddr; |
| |
| if (*ptr) |
| nih_discard (*ptr); |
| } |
| |
| |
| /** |
| * nih_alloc_context_free: |
| * @ctx: context to free. |
| * |
| * This is the internal function called by nih_free(), nih_discard() and |
| * nih_unref() to actually free an allocated context and its attached |
| * objects. |
| * |
| * All parent references must have been discarded prior to calling this |
| * function. |
| * |
| * The destructor for @ctx is called, and then all children are recursively |
| * unreferenced. Those that have no remaining parent references will also |
| * have their destructors called and their children unreferenced, etc. |
| * Once all destructors have been called, the objects themselves are freed. |
| * |
| * Returns: return value from @ptr's destructor, or 0. |
| **/ |
| static inline int |
| nih_alloc_context_free (NihAllocCtx *ctx) |
| { |
| int ret = 0; |
| |
| nih_assert (ctx != NULL); |
| nih_assert (ctx->destructor != NIH_ALLOC_FINALISED); |
| nih_assert (NIH_LIST_EMPTY (&ctx->parents)); |
| |
| /* We have no parents, call our destructor before doing anything |
| * to our children. Save the return value, since this is what |
| * we return. |
| */ |
| if (ctx->destructor) |
| ret = ctx->destructor (NIH_ALLOC_PTR (ctx)); |
| ctx->destructor = NIH_ALLOC_FINALISED; |
| |
| /* Recursively finalise all of our children. */ |
| NIH_LIST_FOREACH_SAFE (&ctx->children, iter) { |
| NihAllocRef *ref = NIH_LIST_ITER (iter, NihAllocRef, |
| children_entry); |
| |
| /* Disassociate the child from its parent. |
| * If that was not the last parent, the child should not |
| * be freed, so destroy the rest of the reference and move |
| * on. |
| */ |
| nih_list_destroy (&ref->parents_entry); |
| if (! NIH_LIST_EMPTY (&ref->child->parents)) { |
| nih_list_destroy (&ref->children_entry); |
| free (ref); |
| continue; |
| } |
| |
| /* Child is to be destroyed and has no links back to its |
| * parents. We call the destructor now. |
| */ |
| if (ref->child->destructor) |
| ref->child->destructor (NIH_ALLOC_PTR (ref->child)); |
| ref->child->destructor = NIH_ALLOC_FINALISED; |
| |
| /* Reparent all of its own children to us so that they too |
| * will be finalised if the last reference is removed. |
| * |
| * In order to do this depth-first while preserving order, |
| * we insert the items before our cursor; and then put the |
| * cursor back at the head of them. |
| */ |
| NIH_LIST_FOREACH_SAFE (&ref->child->children, citer) { |
| NihAllocRef *cref = NIH_LIST_ITER (citer, NihAllocRef, |
| children_entry); |
| |
| nih_list_add (&_iter, &cref->children_entry); |
| } |
| |
| nih_list_add_after (iter, &_iter); |
| } |
| |
| /* We now have a single list of children all of which have no |
| * references back to us as their parent, and all of had their |
| * destructors called. |
| * |
| * Now we free them. |
| */ |
| NIH_LIST_FOREACH_SAFE (&ctx->children, iter) { |
| NihAllocRef *ref = NIH_LIST_ITER (iter, NihAllocRef, |
| children_entry); |
| |
| __nih_free (ref->child); |
| |
| nih_list_destroy (&ref->children_entry); |
| free (ref); |
| } |
| |
| /* And now we can free ourselves. */ |
| __nih_free (ctx); |
| |
| return ret; |
| } |
| |
| |
| /** |
| * nih_alloc_real_set_destructor: |
| * @ptr: pointer to object, |
| * @destructor: destructor function to set. |
| * |
| * Sets the destructor of the allocated object @ptr to @destructor, which |
| * may be NULL to unset an existing destructor. Normally you would use |
| * the nih_alloc_set_destructor() macro which expands to this function |
| * but casts @destructor to the correct type, since almost all destructors |
| * will be defined with their argument to be the type of the object |
| * rather than void *. |
| * |
| * The destructor will be called before the object is freed, either |
| * explicitly by nih_free() or nih_discard(), or because the last parent |
| * has unreferenced the object. |
| * |
| * When the destructor is called, the parent references to the object will |
| * have already been discarded but all children references will be intact |
| * and none of the children will have been freed. There is no need to use |
| * a destructor to unreference or free children, that is automatic. |
| * |
| * The pointer @ptr passed to the destructor is that of the object being |
| * freed, and the destructor may return a value which will be the return |
| * value of nih_free() or nih_discard() if used directly on the object. |
| * |
| * Since objects may also be freed by unreferencing, and the value is not |
| * returned in this case, it should only be used for informational or |
| * debugging purposes. |
| **/ |
| void |
| nih_alloc_real_set_destructor (const void * ptr, |
| NihDestructor destructor) |
| { |
| NihAllocCtx *ctx; |
| |
| nih_assert (ptr != NULL); |
| |
| ctx = NIH_ALLOC_CTX (ptr); |
| nih_assert (ctx->destructor != NIH_ALLOC_FINALISED); |
| |
| ctx->destructor = destructor; |
| } |
| |
| |
| /** |
| * nih_ref: |
| * @ptr: object to reference, |
| * @parent: new parent object. |
| * |
| * Adds a reference to the object @ptr from @parent, adding to any other |
| * objects referencing @ptr. The reference can be broken using nih_unref(). |
| * |
| * @ptr will only be automatically freed when the last parent unreferences |
| * it. It may still be manually freed with nih_free(), though this doesn't |
| * sort out any pointers. |
| * |
| * This function is generally used when accepting an object that you wish |
| * to hold a reference to, which is cheaper than making a copy. The caller |
| * must be careful to only use nih_discard() or nih_unref() to drop its own |
| * reference. |
| **/ |
| void |
| nih_ref (const void *ptr, |
| const void *parent) |
| { |
| nih_assert (ptr != NULL); |
| nih_assert (parent != NULL); |
| |
| nih_alloc_ref_new (NIH_ALLOC_CTX (parent), NIH_ALLOC_CTX (ptr)); |
| } |
| |
| /** |
| * nih_alloc_ref_new: |
| * @parent: parent context, |
| * @child: child context. |
| * |
| * This is the internal function used by nih_ref() and nih_alloc() to |
| * create a new reference between the @parent and @child contexts. |
| * |
| * Returns: new reference, already linked to both objects. |
| **/ |
| static inline NihAllocRef * |
| nih_alloc_ref_new (NihAllocCtx *parent, |
| NihAllocCtx *child) |
| { |
| NihAllocRef *ref; |
| |
| nih_assert (parent != NULL); |
| nih_assert (parent->destructor != NIH_ALLOC_FINALISED); |
| nih_assert (child != NULL); |
| nih_assert (child->destructor != NIH_ALLOC_FINALISED); |
| |
| ref = NIH_MUST (malloc (sizeof (NihAllocRef))); |
| |
| nih_list_init (&ref->children_entry); |
| nih_list_init (&ref->parents_entry); |
| |
| ref->parent = parent; |
| ref->child = child; |
| |
| nih_list_add_after (&parent->children, &ref->children_entry); |
| nih_list_add_after (&child->parents, &ref->parents_entry); |
| |
| return ref; |
| } |
| |
| |
| /** |
| * nih_unref: |
| * @ptr: object to unreference, |
| * @parent: parent object to remove. |
| * |
| * Removes the reference to the object @ptr from @parent, if this is the |
| * last reference to @ptr then @ptr will be automatically freed. |
| * |
| * You never need to call this in your own destructors since children |
| * are unreferenced automatically, however this function is useful if you |
| * only hold a reference to an object for a short period and wish to |
| * discard it. |
| **/ |
| void |
| nih_unref (void * ptr, |
| const void *parent) |
| { |
| NihAllocCtx *ctx; |
| NihAllocRef *ref; |
| |
| nih_assert (ptr != NULL); |
| nih_assert (parent != NULL); |
| |
| ctx = NIH_ALLOC_CTX (ptr); |
| nih_assert (ctx->destructor != NIH_ALLOC_FINALISED); |
| |
| ref = nih_alloc_ref_lookup (NIH_ALLOC_CTX (parent), ctx); |
| |
| nih_assert (ref != NULL); |
| nih_alloc_ref_free (ref); |
| |
| if (NIH_LIST_EMPTY (&ctx->parents)) |
| nih_alloc_context_free (ctx); |
| } |
| |
| /** |
| * nih_unref_only: |
| * @ptr: object to unreference, |
| * @parent: parent object to remove. |
| * |
| * Removes the reference to the object @ptr from @parent, but will not |
| * automatically free @ptr even if this is the last reference. |
| **/ |
| void |
| nih_unref_only (const void *ptr, |
| const void *parent) |
| { |
| NihAllocRef *ref; |
| |
| nih_assert (ptr != NULL); |
| nih_assert (parent != NULL); |
| |
| ref = nih_alloc_ref_lookup (NIH_ALLOC_CTX (parent), |
| NIH_ALLOC_CTX (ptr)); |
| |
| nih_assert (ref != NULL); |
| nih_alloc_ref_free (ref); |
| } |
| |
| /** |
| * nih_alloc_ref_free: |
| * @ref: reference to free. |
| * |
| * This is the internal function used by nih_free(), nih_unref() and |
| * nih_unref_only() to remove the reference @ref from its parent and child |
| * contexts. It does not free the child context, even if this is the last |
| * reference. |
| * |
| * This function is notably not called by nih_alloc_context_unref() since |
| * that manipulates the references to perform finalisation. |
| **/ |
| static inline void |
| nih_alloc_ref_free (NihAllocRef *ref) |
| { |
| nih_assert (ref != NULL); |
| |
| nih_list_destroy (&ref->children_entry); |
| nih_list_destroy (&ref->parents_entry); |
| |
| free (ref); |
| } |
| |
| |
| /** |
| * nih_alloc_parent: |
| * @ptr: object to query, |
| * @parent: parent object to look for. |
| * |
| * If @parent is NULL any parent will match. |
| * |
| * Returns: TRUE if @parent has a reference to @ptr, FALSE otherwise. |
| **/ |
| int |
| nih_alloc_parent (const void *ptr, |
| const void *parent) |
| { |
| NihAllocCtx *ctx; |
| |
| nih_assert (ptr != NULL); |
| |
| ctx = NIH_ALLOC_CTX (ptr); |
| nih_assert (ctx->destructor != NIH_ALLOC_FINALISED); |
| |
| if (parent) { |
| NihAllocRef *ref; |
| |
| ref = nih_alloc_ref_lookup (NIH_ALLOC_CTX (parent), ctx); |
| |
| return ref ? TRUE : FALSE; |
| } else { |
| return NIH_LIST_EMPTY (&ctx->parents) ? FALSE : TRUE; |
| } |
| } |
| |
| /** |
| * nih_alloc_ref_lookup: |
| * @parent: parent context, |
| * @child: child context. |
| * |
| * This is the internal function used by nih_unref() and nih_alloc_parent() |
| * to lookup a reference between the @parent and @child contexts. |
| * |
| * Returns: NihAllocRef structure or NULL if no reference exists. |
| **/ |
| static inline NihAllocRef * |
| nih_alloc_ref_lookup (NihAllocCtx *parent, |
| NihAllocCtx *child) |
| { |
| nih_assert (parent != NULL); |
| nih_assert (parent->destructor != NIH_ALLOC_FINALISED); |
| nih_assert (child != NULL); |
| nih_assert (child->destructor != NIH_ALLOC_FINALISED); |
| |
| NIH_LIST_FOREACH (&child->parents, iter) { |
| NihAllocRef *ref = NIH_LIST_ITER (iter, NihAllocRef, |
| parents_entry); |
| |
| if (ref->parent == parent) |
| return ref; |
| } |
| |
| return NULL; |
| } |
| |
| |
| /** |
| * nih_alloc_size: |
| * @ptr: pointer to object. |
| * |
| * Returns: the size of the allocated object, which may be larger than |
| * originally requested. |
| **/ |
| size_t |
| nih_alloc_size (const void *ptr) |
| { |
| NihAllocCtx *ctx; |
| |
| nih_assert (ptr != NULL); |
| |
| ctx = NIH_ALLOC_CTX (ptr); |
| nih_assert (ctx->destructor != NIH_ALLOC_FINALISED); |
| |
| return malloc_usable_size (ctx) - sizeof (NihAllocCtx); |
| } |