/* Jitter: memory heap data structure header.
Copyright (C) 2018, 2019 Luca Saiu
Updated in 2020 by Luca Saiu
Written by Luca Saiu
This file is part of Jitter.
Jitter is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jitter 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jitter. If not, see . */
#ifndef JITTER_HEAP_H_
#define JITTER_HEAP_H_
#include /* For feature macros. */
#if defined (JITTER_HAVE_ALIGNAS)
# include /* For alignas. */
#endif // defined (JITTER_HAVE_ALIGNAS)
#include /* For offsetof. */
#include
#include
#include
/* This is a memory handling facility.
* ************************************************************************** */
/* This is a heap in the sense of a data structure for implementing dynamic
memory allocation and freeing of buffers from larger blocks. A heap in
this sense is sometimes called a "memory pool".
This has nothing to do with the tree data structure imposing ordering
constraints, also called "heap". */
// FIXME: a few words about policies (first-fit in a LIFO-ordered non-segregated
// free list [hole list here], immediate coalescing), single blocks and heaps.
// Wilson's survey citing his own work [p. 68, left column seems to strongly
// favor FIFO over LIFO. Wilson's arguments seem very reasonable; I am only
// worried about stronger cache effects on current machines, 25 years after
// his publication.
// I can experiment quite easily with FIFO, or conditionalize.]
/* Heap blocks and things.
* ************************************************************************** */
/* A "block" is a memory interval containing a header, plus space for the
objects to be allocated. Every object in a block lives within its memory
space, without relying on external memory allocation facilities. The same
block memory also contains every bookkeeping data structure, again without
relying on external memory. */
/* An alignment where it is safe for both header structures and payloads to
begin.
This *must* be a power of two. */
#define JITTER_HEAP_ALIGNMENT \
8 // Eight bytes should be enough for every architecture.
/* Each heap block contains a header and then a sequence of "things", each
aligned to JITTER_HEAP_ALIGNMENT bytes and all living within the block
memory.
Each thing can be:
- a terminator (a special marker at the beginning and end of each block);
- a hole (currently unused memory);
- an object (currently used memory).
Holes within a single block are linked together in a doubly-linked list,
always preceded by the left terminator and followed by the right
terminator.
Currently, the last freed block is the first in the list (after the
left terminator).
Every thing, independently from its tag, contains a payload_size field
allowing to navigate things within a block left-to-right, ad a
thing_on_the_left (tagged) pointer, allowing to navigate right-to-left. */
/* A thing header and the beginning of its payload. Every thing pointer is
aliged to JITTER_HEAP_ALIGNMENT bytes. Since every thing header stores a
pointer to the previous thing (in address order) within the same block and
its payload size, it is always possible to navigate from one thing to its
neighbors in both directions; this is useful for hole coalescing.
This struct is used for things allocated on blocks, and for big objects as
well. */
struct jitter_heap_thing
{
/* The first field should have pointer type, so that we can guarantee that if
the structure is aligned correctly, then no 64-bit pointer will be accessed
unaligned. The following fields should get correctly aligned for free. */
/* A tagged pointer, whose two least significant bits hold a value of type
enum jitter_heap_thing_tag. The tag encodes the type of the current thing,
and not of the thing on the left.
The pointer itself is aligned to at least JITTER_HEAP_ALIGNMENT bytes, and
its untagged value can be extracted by masking off the two least
significant bits or by subtracting a known tag; no shifting is required.
After untagging this pointer points to the header for the thing immediately
to the left of the current thing, or is NULL for a left terminator.
This is NULL, apart from the tag, for big objects. */
struct jitter_heap_thing *thing_on_the_left;
/* The payload size, including the anonymous union below and possibly
extending further; the byte at (payload + payload_size_in_bytes) is right
past the allocated size of this thing and marks the beginning of the next
thing on the same block, if any. */
size_t payload_size_in_bytes;
/* Which of the following field is used depends on the thing tag. */
alignas (JITTER_HEAP_ALIGNMENT)
union
{
/* Doubly-linked-list pointers to the previous and next hole in the block.
This is only used for holes. */
struct jitter_list_links hole_links;
/* The beginning of the payload, which can actually extend further than the
size declared here. */
char payload [sizeof (struct jitter_list_links)];
/* /\* This unused field should force the anonymous union to be aligned in a */
/* conservative way, so that we can store any reasonable datum as an object */
/* payload. *\/ */
/* double unused; */
};
};
/* The number of bytes occupied by the initial header in a block, plus padding.
In other words, this is the offset from the header beginning to the payload
beginning in each object. */
#define JITTER_HEAP_HEADER_OVERHEAD \
(offsetof (struct jitter_heap_thing, payload)) \
/* The size for the smallest possible thing. The "size of a thing" always
includes the header as well, and not only the payload. */
#define JITTER_HEAP_MINIMUM_THING_SIZE \
(sizeof (struct jitter_heap_thing))
/* The minimum payload (or links) size for a thing in bytes, independently from
its tag. */
#define JITTER_HEAP_MINIMUM_PAYLOAD_SIZE \
(sizeof (struct jitter_heap_thing) \
- JITTER_HEAP_HEADER_OVERHEAD)
/* Given an expression evaluating to a pointer, expand to an expression
evaluting to the same pointer or the next possible smaller value respecting
JITTER_HEAP_ALIGNMENT. */
#define JITTER_HEAP_ALIGN_LEFT(p) \
((void *) \
JITTER_PREVIOUS_MULTIPLE_OF_POWER_OF_TWO((jitter_uint) (p), \
JITTER_HEAP_ALIGNMENT))
/* Like JITTER_HEAP_ALIGN_LEFT, but evaluate to the same pointer or the next
possible *larger* value. */
#define JITTER_HEAP_ALIGN_RIGHT(p) \
((void *) \
JITTER_NEXT_MULTIPLE_OF_POWER_OF_TWO((jitter_uint) (p), \
JITTER_HEAP_ALIGNMENT))
/* Every used configuration of the tag associated to the pointer field in object
headers. The tag is JITTER_HEAP_THING_TAG_BIT_NO bits wide. */
enum jitter_heap_thing_tag
{
/* The header is for a hole. */
jitter_heap_thing_tag_hole = 0,
/* The header is for an object. */
jitter_heap_thing_tag_object = 1,
/* The header is for a block terminator, without distinction between left and
right. */
jitter_heap_thing_tag_terminator = 2,
/* The header for a big object. Big objects don't live in blocks, but are
allocated each in its own buffer, controlled by heaps. See below. */
jitter_heap_thing_tag_big = 3
};
/* How many bits are reserved for a tag within a tagged pointer. Right now,
with objects aligned to 8 bytes on every architectures, I could already
afford 3 tag bits for free, even if 2 suffice. By simply enforcing a wider
alignment I could make even more tag bits available. */
#define JITTER_HEAP_THING_TAG_BIT_NO \
2
/* A bitmask having 1 bits where the tag is in a pointer, 0 elsewhere. */
#define JITTER_HEAP_THING_TAG_BIT_MASK \
((jitter_uint) ((1 << JITTER_HEAP_THING_TAG_BIT_NO) - 1))
/* A bitmask having 0 bits where the tag is in a pointer, 1 elsewhere. */
#define JITTER_HEAP_THING_NON_TAG_BIT_MASK \
(~ JITTER_HEAP_THING_TAG_BIT_MASK)
/* Given an expression evaluating to an untagged pointer, expand to an
expression evaluating to the same pointer with the given tag. The expansion
may evaluate p multiple times. */
#define JITTER_HEAP_TAG_POINTER(p, tag) \
((struct jitter_heap_thing *) \
(((jitter_uint) (p)) | ((jitter_uint) (tag))))
/* Given an expression evaluating to a tagged pointer and an expression
evaluating to its tag, assumed to be the same, expand to an expression
evaluating to the same untagged pointer. The expansion may evaluate the
macro arguments multiple times.
This is the preferred way of untagging a pointer: often the untagging can be
done at zero costs, as GCC can generate a load instruction with a constant
offset to account for the quantity to subtract. */
#define JITTER_HEAP_UNTAG_POINTER_KNOWN_TAG(p, tag) \
((struct jitter_heap_thing *) \
(((jitter_uint) (p)) - ((jitter_uint) (tag))))
/* Given an expression evaluating to a tagged pointer, an expression
evaluating to its current tag assumed to be the the given one, and a third
expression evaluating to a new tag, expand to an expression evaluating to
the same pointer tagged with the new tag instead of the current one.
The expansion may evaluate the macro arguments multiple times. */
#define JITTER_HEAP_RETAG_POINTER(p, old_tag, new_tag) \
((struct jitter_heap_thing *) \
(((jitter_uint) (p)) \
- ((jitter_uint) (old_tag)) \
+ ((jitter_uint) (new_tag))))
/* Given an expression evaluating to a tagged pointer expand to an expression
evaluating to the same untagged pointer. The expansion may evaluate the
macro arguments multiple times.
This is usually less efficient than JITTER_HEAP_UNTAG_POINTER_KNOWN_TAG. */
#define JITTER_HEAP_UNTAG_POINTER(p) \
((struct jitter_heap_thing *) \
(((jitter_uint) (p)) & JITTER_HEAP_THING_NON_TAG_BIT_MASK))
/* Given an expression evaluating to a tagged pointer expand to an expression
evaluating to its tag. The expansion may evaluate the macro argument
multiple times. */
#define JITTER_HEAP_POINTER_TAG(p) \
((enum jitter_heap_thing_tag) \
(((jitter_uint) (p)) & JITTER_HEAP_THING_TAG_BIT_MASK))
/* Expand to an expression evaluating to the tag of the thing pointed to by the
result of the given expression, which should evaluate to an object of type
struct jitter_heap_thing * . */
#define JITTER_HEAP_GET_TAG(thing) \
(JITTER_HEAP_POINTER_TAG ((thing)->thing_on_the_left))
/* Expand to an expression evaluating to the payload of the result of the given
expression, which should evaluate to an object of type
struct jitter_heap_thing * .
The argument may be evaluated multiple times. */
#define JITTER_HEAP_THING_TO_PAYLOAD(thing) \
((thing)->payload)
/* Expand to an expression evaluating to a the pointer of the thing whose
initial payload pointer is the result of the given expression.
The argument may be evaluated multiple times. */
#define JITTER_HEAP_PAYLOAD_TO_THING(p) \
((struct jitter_heap_thing *) \
(((char *) (p)) - JITTER_HEAP_HEADER_OVERHEAD))
/* Expand to an expression evaluating to a pointer to the thing on the left of
the thing pointed by the result of the given expression, which should
evaluate to an object of type struct jitter_heap_thing * .
The argument may be evaluated multiple times. */
#define JITTER_HEAP_THING_ON_THE_LEFT_OF(thing) \
(JITTER_HEAP_UNTAG_POINTER((thing)->thing_on_the_left))
/* Expand to an expression evaluating to a pointer to the thing on the right of
the thing pointed by the result of the given expression, which should
evaluate to an object of type struct jitter_heap_thing * .
The argument may be evaluated multiple times. */
#define JITTER_HEAP_THING_ON_THE_RIGHT_OF(thing) \
((struct jitter_heap_thing *) \
(((char *) JITTER_HEAP_THING_TO_PAYLOAD (thing)) \
+ (thing)->payload_size_in_bytes))
/* A "block" is a data structure written at or near the beginning of some
allocated memory, followed by things living in the same memory space. */
struct jitter_heap_block
{
/* The first field should have pointer type; see the comment above. */
/* A pointer to the allocated memory for the block, which is not necessarily
the same as a pointer to this structure, as there may be alignment
constraints. The memory to be freed when the block is released is the one
referred by this pointer. */
void *allocated_space;
/* A doubly linked list containing:
- the left terminator;
- all the holes in this block, in an unspecified order;
- the right terminator.
The left and right terminators never change after block initialization, and
therefore the list is "always-nonempty" in the sense of jitter-list.h .
This assumption makes list update operations much more efficient. */
struct jitter_list_header hole_list;
/* How many bytes were allocated, including any bytes wasted by misalignment. */
size_t allocated_space_size_in_bytes;
/* Links to the previous and next block within a heap. */
struct jitter_list_links block_links;
/* Having the left terminator directly accessible as a field is convenient to
walk thru the hole list, without dereferencing hole_list->first and then skipping
the first element every time.
The right terminator cannot be stored in the same way, as its offset from
the beginning of the block header depends on the block size. */
alignas (JITTER_HEAP_ALIGNMENT)
struct jitter_heap_thing left_terminator;
/* There must be no other field after the left terminator: other things get
allocated in the memory buffer holding this data structure right after the
left terminator. */
};
/* Object allocation, deallocation and reallocation from a given block.
* ************************************************************************** */
/* The functions in this section work on object payload pointers, as seen by the
user; internal object (and hole) headers are invisible. */
/* Given a heap block, return a pointer to a freshly allocated object within the
block having the given payload size (or higher, to satisfy alignment
constraints). Return NULL if there is no single hole large enough to satisy
the request.
The returned pointer is aligned to JITTER_HEAP_ALIGNMENT bytes. */
void *
jitter_heap_allocate_from_block (struct jitter_heap_block *b,
size_t size_in_bytes)
__attribute__ ((nonnull (1), malloc,
alloc_size (2),
assume_aligned (JITTER_HEAP_ALIGNMENT)));
/* Given a heap block and a pointer to an existing object within it, return a
pointer to a new object of the given new size having the same content of the
pointed existing object up to the given new size, and free the existing
object. Return NULL if there is no single hole large enough to satisy the
request.
The returned pointer may be the same as old_payload or the new object may
otherwise reuse the same space occupied by the old one, but in the general
case it is invalid to keep using old_payload after this call returns a
non-NULL value.
The returned pointer is aligned to JITTER_HEAP_ALIGNMENT bytes. */
void *
jitter_heap_reallocate_from_block (struct jitter_heap_block *b,
void *old_payload,
size_t new_size_in_bytes)
__attribute__ ((nonnull (1, 2),
alloc_size (3),
assume_aligned (JITTER_HEAP_ALIGNMENT),
warn_unused_result));
/* Given a heap block and an initial pointer to the payload of an object
allocated on the block, free the object, compacting any holes around it. */
void
jitter_heap_free_from_block (struct jitter_heap_block *p,
void *object_on_p)
__attribute__ ((nonnull (1, 2)));
/* Big objects.
* ************************************************************************** */
/* Big objects do not live on heap blocks: they are allocated individually, on
demand, with the primitives supplies by the user in a heap header and
released immediately as soon as the user frees. This, when the primitive
relies on mmap, will let the memory be immediately returned to the operating
system. */
/* Every big object has a header just like heap things living in blocks, for
format compatibility. In order to be able to distinguish big and non-big
objects at run time big objects have a distinct tag (see the definition of
enum jitter_heap_thing_tag above).
Every big object in a heap belongs to a doubly linked list, distinct from the
list of objects in a block and from the list of blocks. Since I don't want
to add two fields in the header of *every* object, wasting space on non-big
objects as well, big objects have a "big pre-header", which is allocated in
memory just before the ordinary object header, at a fixed negative offset. */
/* The pre-header for big objects, also containing the object header, structured
like the header of non-big objects, which in its turn includes the beginning
of the object payload.
The pre-header is aligned to JITTER_HEAP_ALIGNMENT bytes, and so are its
internal header and the payload within the header. */
struct jitter_heap_big
{
/* Doubly-linked list pointers to the previous and next big objects in
the same heap. */
struct jitter_list_links big_links;
/* The ordinary thing header, containing a jitter_heap_thing_tag_big tag
and NULL as the thing_on_the_left pointer. */
alignas (JITTER_HEAP_ALIGNMENT)
struct jitter_heap_thing thing;
/* There must be nothing else after the header: the header actually includes
the beginning of the payload, which must not be interrupted by other fields
before the rest of the payload. */
};
/* The thing_on_the_left field value for every big object, which is to say a
NULL pointer tagged with the jitter_heap_thing_tag_big tag. */
#define JITTER_HEAP_BIG_THING_ON_THE_LEFT \
((struct jitter_heap_thing *) \
(((jitter_uint) NULL) \
| (jitter_uint) jitter_heap_thing_tag_big))
/* Given an expression evaluating to an initial pointer to an object payload,
expand to an expression evaluating to non-false iff the object is big.
Implementation note: this is faster than using JITTER_HEAP_GET_TAG: I can
avoid the masking operation by relying on the fact that the thing_on_the_left
pointer is always (tagged) NULL for big objects. */
#define JITTER_HEAP_IS_PAYLOAD_BIG(payload) \
((JITTER_HEAP_PAYLOAD_TO_THING(payload)->thing_on_the_left) \
== JITTER_HEAP_BIG_THING_ON_THE_LEFT)
/* The overhead of a big pre-header in bytes, or in other words the offset from
the beginning of the big pre-haeder to the beginning of the thing header.
The big pre-header overhead doesn't include the thing header size. */
#define JITTER_HEAP_BIG_PRE_HEADER_OVERHEAD \
(offsetof (struct jitter_heap_big, thing))
/* The total overhead of big pre-header plus thing header for a big object, or
in other words the offset from the beginning of the big pre-haeder to the
beginning of the payload within the thing within the big pre-header. */
#define JITTER_HEAP_BIG_TOTAL_OVERHEAD \
(JITTER_HEAP_BIG_PRE_HEADER_OVERHEAD \
+ JITTER_HEAP_HEADER_OVERHEAD)
/* Given an expression evaluating to an initial pointer to an big object
payload, expand to an expression evaluating to a pointer to the object
big pre-header. */
#define JITTER_HEAP_PAYLOAD_TO_BIG(payload) \
((struct jitter_heap_big *) \
(((char *) (payload)) - JITTER_HEAP_BIG_TOTAL_OVERHEAD))
/* Given an expression evaluating to an initial pointer to an big object
payload, expand to an expression evaluating to a pointer to the object
big pre-header. */
#define JITTER_HEAP_BIG_TO_PAYLOAD(bigp) \
((void *) (((char *) (bigp)) + JITTER_HEAP_BIG_TOTAL_OVERHEAD))
/* A forward-declaration. */
struct jitter_heap;
/* Allocate a fresh big object of the given user payload size from the pointed
heap. Return an initial pointer to its payload.
This function is slightly more efficient than general heap allocation, and
can be used when the user is sure that the required object will be big. */
void *
jitter_heap_allocate_big (struct jitter_heap *h,
size_t user_payload_size_in_bytes)
__attribute__ ((nonnull (1), returns_nonnull));
/* Given a heap pointer and an initial pointer to the payload of a big object
belonging to it, free the big object. This doesn't check that the payload
actually belongs to a suitable object, but should be slightly faster than
the general freeing function. */
void
jitter_heap_free_big (struct jitter_heap *h, void *big_payload)
__attribute__ ((nonnull (1)));
/* Reallocation is not supported for big objects. For the time being I will
assume that it's a non-critical operation in this case. */
/* Heaps.
* ************************************************************************** */
/* A "heap" as intended here is a data structure holding an arbitrary number of
blocks, each block holding objects, along with information about how to make
and destroy blocks.
Every block in the heap will have the same length, and will be aligned to
this length. This makes it possible, given any object address within the
block, to find the block with a quick bitwise masking operation.
A heap is a convenient abstraction to allocate, deallocate and reallocate
objects from blocks which are automatically made and destroyed as needed; if
an allocated object is too big to fit in a blog, heap allocation and
reallocation functions will automatically make a big object instead.
Of course operations within an existing block are assumed to be more
efficient than operations altering the number of blocks, which usually
require syscalls. Heap operations will attempt to reuse existing blocks as
far as possible. */
/* Here come some types for functions to be supplied by the user, defining a
block "kind". */
/* A function allocating fresh memory for a block, taking a size in bytes and
returning a fresh block of at least the required size, or NULL on allocation
failure. */
typedef void * (* jitter_heap_primitive_allocate_function)
(size_t size_in_bytes);
/* A function destroying an existing block, taking the block as returned by the
appropriate jitter_heap_primitive_allocate_function function and the
allocated size as it was passed at making time. */
typedef void (* jitter_heap_primitive_free_function)
(void *allocated_memory, size_t size_in_bytes);
/* A descriptor for heap objects, specifying its allocation primitives. The
same descriptor might be used for multiple heaps, but it's relatively
inconvenient for the user to specify it; therefore structures of this type
are automatically filled by jitter_heap_initialize , and are effectively
invisible to the user. */
struct jitter_heap_descriptor
{
/* A function allocating a fresh block or big object. */
jitter_heap_primitive_allocate_function make;
/* A function destroying an entire existing block or big object. */
jitter_heap_primitive_free_function destroy;
/* The "natural" alignment in bytes which is always and automatically
guaranteed by the make allocation primitive, pointed by the field above. */
size_t make_natural_alignment_in_bytes;
/* A function deallocating only a *part* for a block allocated by the make
primitive pointed above. This can be NULL if no suitable primitive for
unmapping part of a buffer exists; otherwise the API will be just like an
munmap with no result. */
jitter_heap_primitive_free_function unmap_part_or_NULL;
/* The desired block size in bytes. This constraint is, in general, respected
by allocating larger blocks using the make primitive pointed above, and if
possible unmapping a part of the buffer. */
size_t block_size_and_alignment_in_bytes;
/* The block bit mask, having 1 bits for the bits identifying a block and
0 bits for the bits which are part of an offset within the block. */
jitter_uint block_bit_mask;
/* The size in bytes of the smallest payload large enough to belong to a big
object. */
size_t block_size_smallest_big_payload_in_bytes;
};
/* A data structure encoding a heap. The user will initialize a structure
of this type and use it for allocating and freeing. */
struct jitter_heap
{
/* A descriptor for this heap. This small struct is copied rather than
pointed, to avoid an indirection on time-critical heap operations on
objects. */
struct jitter_heap_descriptor descriptor;
/* The list of all the blocks in this current heap. This is never empty, but
the list cannot be considered "always-nonempty" as per jitter-list.h ,
since this doesn't use terminator elements and the first and last elements
of the list can change. */
// FIXME: possibly make this always-nonempty, by adding two dummy (unaligned)
// blocks as elements within this struct. In this case the terminators don't
// need to be in a specific order by address, differently from thing
// terminators within a block. Is this critical enough? Maybe not.
struct jitter_list_header block_list;
/* The list of all the big objects in this heap. */
// FIXME: possibly make this always-nonempty as well.
struct jitter_list_header big_list;
/* A pointer to the current block, from which we are allocating by default.
This is never NULL, and is always equal to the first element of the list;
however we save indirections by keeping a pointer here as an
optimization. */
struct jitter_heap_block *default_block;
};
/* Initialize the pointed heap to use the pointed descriptor elements. A
suitable descriptor, stored in the heap, is initialized automatically and
completed by computing values for the remaining fields from the given
values.
The given value for block_size_and_alignment_in_bytes here is a lower bound:
the actual block size will be increased as needed in order for it to be
a power of two, and a multiple of make_natural_alignment_in_bytes . */
void
jitter_heap_initialize (struct jitter_heap *h,
jitter_heap_primitive_allocate_function make,
jitter_heap_primitive_free_function destroy,
size_t make_natural_alignment_in_bytes,
jitter_heap_primitive_free_function unmap_part_or_NULL,
size_t block_size_and_alignment_in_bytes)
__attribute__ ((nonnull (1, 2, 3)));
/* Finalize the pointed heap, destroying every block it contains. */
void
jitter_heap_finalize (struct jitter_heap *h)
__attribute__ ((nonnull (1)));
/* Object allocation, deallocation and reallocation from a given heap.
* ************************************************************************** */
/* These function are similar to their _from_block counterparts, but take a heap
pointer instead of a block pointer.
These are the main user functions for working with allocation, reallocation
and freeing of objects. The returned payloads may be big objects: the user
sees no difference.
Allocation and reallocation never return NULL: failure is fatal. */
/* Given a heap, return a pointer to a freshly allocated object within the
heap having at least the given payload size (or higher, to satisfy alignment
constraints). Fail fatally if allocation is not possible.
The returned pointer is aligned to JITTER_HEAP_ALIGNMENT bytes. */
void *
jitter_heap_allocate (struct jitter_heap *h, size_t size_in_bytes)
__attribute__ ((nonnull (1), malloc, returns_nonnull,
alloc_size (2),
assume_aligned (JITTER_HEAP_ALIGNMENT)));
/* Given a heap pointer and a pointer to an existing object within it, return a
pointer to a new object of the given new size having the same content of the
pointed existing object up to the given new size, and free the existing
object. Fail fatally if there is not enough space and creating a new block
fails. The returned pointer may be the same as old_payload or the new object
may otherwise reuse the same space occupied by the old one, but in the
general case it is invalid to keep using old_payload after this call returns.
The returned pointer is aligned to JITTER_HEAP_ALIGNMENT bytes. */
void *
jitter_heap_reallocate (struct jitter_heap *h, void *old_payload,
size_t new_size_in_bytes)
__attribute__ ((nonnull (1, 2), returns_nonnull, alloc_size (3),
assume_aligned (JITTER_HEAP_ALIGNMENT), warn_unused_result));
/* Given a heap pointer and a pointer to an existing object within it, free
space after the object by shrinking it to the given new size in bytes, which
must not be less than the current object allocated size, rounded up as
required by the implementation. If freeing up space is not possible do
nothing, without failing. */
void
jitter_heap_shrink_in_place (struct jitter_heap *h, void *payload,
size_t new_size_in_bytes)
__attribute__ ((nonnull (1, 2)));
/* Given a heap and an initial pointer to the payload of an object from the
heap, free the object. */
void
jitter_heap_free (struct jitter_heap *h, void *object_payload)
__attribute__ ((nonnull (1, 2)));
#endif // #ifndef JITTER_HEAP_H_