Graphviz 13.1.3~dev.20250905.1412
|
type-generic dynamically expanding list More...
Go to the source code of this file.
Macros | |
#define | LIST(type) |
#define | LIST_DTOR_FREE ((void *)1) |
#define | LIST_SIZE(list) gv_list_size_((list)->impl) |
#define | LIST_IS_EMPTY(list) (LIST_SIZE(list) == 0) |
#define | LIST_TRY_APPEND(list, item) |
#define | LIST_APPEND(list, item) |
#define | LIST_PREPEND(list, item) |
#define | LIST_GET(list, index) ((list)->base[gv_list_get_((list)->impl, (index))]) |
#define | LIST_AT(list, index) (&(list)->base[gv_list_get_((list)->impl, (index))]) |
#define | LIST_FRONT(list) LIST_AT((list), 0) |
#define | LIST_BACK(list) LIST_AT((list), LIST_SIZE(list) - 1) |
#define | LIST_SET(list, index, item) |
#define | LIST_REMOVE(list, item) |
#define | LIST_CLEAR(list) |
#define | LIST_RESERVE(list, capacity) gv_list_reserve_(&(list)->impl, capacity, sizeof((list)->base[0])) |
#define | LIST_CONTAINS(list, needle) |
#define | LIST_COPY(dst, src) |
#define | LIST_IS_CONTIGUOUS(list) gv_list_is_contiguous_((list)->impl); |
#define | LIST_SYNC(list) gv_list_sync_(&(list)->impl, sizeof((list)->base[0])) |
#define | LIST_SORT(list, cmp) gv_list_sort_(&(list)->impl, (cmp), sizeof((list)->base[0])) |
#define | LIST_REVERSE(list) gv_list_reverse_(&(list)->impl, sizeof((list)->base[0])) |
#define | LIST_SHRINK_TO_FIT(list) gv_list_shrink_to_fit_(&(list)->impl, sizeof((list)->base[0])) |
#define | LIST_FREE(list) |
#define | LIST_PUSH_BACK(list, item) LIST_APPEND((list), (item)) |
#define | LIST_POP_FRONT(list) |
#define | LIST_POP_BACK(list) |
#define | LIST_DETACH(list, datap, sizep) |
The code in this header is structured as a public API made up of macros that do as little as possible before handing off to internal functions.
If you are familiar with the concept of a dynamically expanding array like C++’s std::vector
, the only things that will likely throw you off are:
LIST
is implemented through a union
that overlaps the base
member of a list_t_
(a generic list core) with a typed pointer. This design involves the public macros dealing in the LIST(foo)
type while the private functions deal in list_t_
s.try_reserve
to better understand this.Some general terminology you may see in function/macro names: base – the start of the underlying heap allocation backing a list dtor – destructor head – slot index of the start of a list item – a list element slot – an item-sized space in the list, offset recorded from base
Some unorthodox idioms you may see used in this file: • (void)(foo == bar)
as a way to force the compiler to type-check that foo
and bar
have compatible types. This is the best we can do for pointer compatibility checks without typeof
. • (void)(sizeof(foo) == sizeof(bar) ? (void)0 : (void)(…,abort())
as an even weaker version of the above, for when we need to delay a check to runtime instead of compile-time. This is a very unreliable check for foo
and bar
being the same type, so should be avoided wherever possible.
Definition in file list.h.
#define LIST | ( | type | ) |
#define LIST_APPEND | ( | list, | |
item | |||
) |
add an item to the end of a list
You can think of this macro as having the C type:
void LIST_APPEND(LIST(<type>) *list, <type> item);
This macro succeeds or exits on out-of-memory; it never return failure.
Note, in contrast to LIST_TRY_APPEND
, gv_list_append_slot_
and the write to (list)->base[…]
are in separate statements because the gv_list_append_slot_
call here can alter (list)->base
.
list | List to operate on |
item | Element to append |
#define LIST_AT | ( | list, | |
index | |||
) | (&(list)->base[gv_list_get_((list)->impl, (index))]) |
retrieve a pointer to an item from a list
You can think of this macro as having one of the C types:
<type> *LIST_AT(LIST(<type>) *list, size_t index); const <type> *LIST_AT(const LIST(<type>) *list, size_t index);
list | List to operate on |
index | Item index to get |
retrieve a pointer to the last item in a list
You can think of this macro as having one of the C types:
<type> *LIST_BACK(LIST(<type>) *list); const <type> *LIST_BACK(const LIST(<type>) *list);
list | List to operate on |
#define LIST_CLEAR | ( | list | ) |
remove all items from a list
You can think of this macro as having the C type:
void LIST_CLEAR(LIST(<type>) *list);
list | List to clear |
#define LIST_CONTAINS | ( | list, | |
needle | |||
) |
does a list contain a given item?
You can think of this macro as having the C type:
bool LIST_CONTAINS(const LIST(<type>) *list, <type> needle);
The needle
parameter must be an expression that can have its address taken. E.g. LIST_CONTAINS(my_ints, 2)
is not valid. This can be worked around with C99 compound literals, LIST_CONTAINS(my_ints, (int){2})
.
list | List to search |
needle | Item to search for |
#define LIST_COPY | ( | dst, | |
src | |||
) |
copy a list
You can think of this macro as having the C type:
void LIST_COPY(LIST(<type>) *dst, const LIST(<type>) *src);
[out] | dst | Copy of the source list on completion |
src | List to copy |
#define LIST_DETACH | ( | list, | |
datap, | |||
sizep | |||
) |
transform a managed list into a bare array
You can think of this macro as having the C type:
void LIST_DETACH(LIST(<type>) *list, <type> **data, size_t *size);
This can be useful when needing to pass data to a callee who does not use this API. The managed list is emptied and left in a state where it can be reused for other purposes.
list | List to operate on | |
[out] | data | The list data on completion |
[out] | size | The list size on completion |
#define LIST_DTOR_FREE ((void *)1) |
sentinel value to indicate you want free
to be used as a list destructor
Sample usage:
LIST(char *) my_strings = {.dtor = LIST_DTOR_FREE};
#define LIST_FREE | ( | list | ) |
free resources associated with a list
You can think of this macro as having the C type:
void LIST_FREE(LIST(<type>) *list);
After a call to this function, the list is empty and may be reused.
list | List to free |
#define LIST_FRONT | ( | list | ) | LIST_AT((list), 0) |
retrieve a pointer to the first item in a list
You can think of this macro as having one of the C types:
<type> *LIST_FRONT(LIST(<type>) *list); const <type> *LIST_FRONT(const LIST(<type>) *list);
list | List to operate on |
#define LIST_GET | ( | list, | |
index | |||
) | ((list)->base[gv_list_get_((list)->impl, (index))]) |
retrieve an item from a list
You can think of this macro as having the C type:
<type> LIST_GET(const LIST(<type>) *list, size_t index);
list | List to operate on |
index | Item index to get |
#define LIST_IS_CONTIGUOUS | ( | list | ) | gv_list_is_contiguous_((list)->impl); |
does the list not wrap past its end?
This checks whether the list is discontiguous in how its elements appear in memory:
┌───┬───┬───┬───┬───┬───┬───┬───┐
a contiguous list: │ │ │ w │ x │ y │ z │ │ │ └───┴───┴───┴───┴───┴───┴───┴───┘ 0 1 2 3
┌───┬───┬───┬───┬───┬───┬───┬───┐ a discontiguous list: │ y │ z │ │ │ │ │ x │ y │ └───┴───┴───┴───┴───┴───┴───┴───┘ 2 3 0 1
You can think of this macro as having the C type:
bool LIST_IS_CONTIGUOUS(const LIST(<type>>) *list);
list | List to inspect |
#define LIST_IS_EMPTY | ( | list | ) | (LIST_SIZE(list) == 0) |
does this list contain no elements?
You can think of this macro as having the C type:
bool LIST_IS_EMPTY(const LIST(<type>) *list);
list | List to inspect |
#define LIST_POP_BACK | ( | list | ) |
remove and return the last item of a list
You can think of this macro as having the C type:
<type> LIST_POP_BACK(LIST(<type>) *list);
list | List to operate on |
#define LIST_POP_FRONT | ( | list | ) |
remove and return the first item of a list
You can think of this macro as having the C type:
<type> LIST_POP_FRONT(LIST(<type>) *list);
list | List to operate on |
#define LIST_PREPEND | ( | list, | |
item | |||
) |
add an item to the beginning of a list
You can think of this macro as having the C type:
void LIST_PREPEND(LIST(<type>) *list, <type> item);
This macro succeeds or exits on out-of-memory; it never return failure.
list | List to operate on |
item | Element to prepend |
#define LIST_PUSH_BACK | ( | list, | |
item | |||
) | LIST_APPEND((list), (item)) |
alias for append
You can think of this macro as having the C type:
void LIST_PUSH_BACK(LIST(<type>) *list, <type> item);
list | List to operate on |
item | Item to append |
#define LIST_REMOVE | ( | list, | |
item | |||
) |
remove an item from a list
You can think of this macro as having the C type:
void LIST_REMOVE(LIST(<type>) *list, <type> item);
list | List to operate on |
item | Item to remove |
#define LIST_RESERVE | ( | list, | |
capacity | |||
) | gv_list_reserve_(&(list)->impl, capacity, sizeof((list)->base[0])) |
reserve space for new items in a list
You can think of this macro as having the C type:
void LIST_RESERVE(LIST(<type>) *list, size_t capacity);
list | List to operate on |
capacity | Total number of item slots to make available |
#define LIST_REVERSE | ( | list | ) | gv_list_reverse_(&(list)->impl, sizeof((list)->base[0])) |
reverse the item order of a list
You can think of this macro as having the C type:
void LIST_REVERSE(LIST(<type>) *list);
list | List to operate on |
#define LIST_SET | ( | list, | |
index, | |||
item | |||
) |
update the value of an item in a list
You can think of this macro as having the C type:
void LIST_SET(LIST(<type>) *list, size_t index, <type> item);
list | List to operate on |
index | Index of item to update |
item | New value to set |
#define LIST_SHRINK_TO_FIT | ( | list | ) | gv_list_shrink_to_fit_(&(list)->impl, sizeof((list)->base[0])) |
decrease the allocated capacity of a list to minimum
You can think of this macro as having the C type:
void LIST_SHRINK_TO_FIT(LIST(<type>) *list);
list | List to operate on |
#define LIST_SIZE | ( | list | ) | gv_list_size_((list)->impl) |
get the number of elements in a list
You can think of this macro as having the C type:
size_t LIST_SIZE(const LIST(<type>) *list);
list | List to inspect |
#define LIST_SORT | ( | list, | |
cmp | |||
) | gv_list_sort_(&(list)->impl, (cmp), sizeof((list)->base[0])) |
sort a list
You can think of this macro as having the C type:
void LIST_SORT(LIST(<type>) *list, int (*comparator)(const void *a, const void *b));
list | List to operate on |
comparator | How to compare two list items |
#define LIST_SYNC | ( | list | ) | gv_list_sync_(&(list)->impl, sizeof((list)->base[0])) |
shuffle the populated contents to reset head
to 0
You can think of this macro as having the C type:
void LIST_SYNC(LIST(<type>) *list);
See the LIST_IS_CONTIGUOUS
leading comment for a better understanding of what it means for head
to be non-zero.
list | List to operate on |
#define LIST_TRY_APPEND | ( | list, | |
item | |||
) |
try to append a new item to a list
You can think of this macro as having the C type:
bool LIST_TRY_APPEND(LIST(<type>) *list, <type> item);
Note that referencing (list)->base
and calling gv_list_append_slot_
within a single expression without an intervening sequence point is OK because, after the preceding reservation probes, we know gv_list_append_slot_
will not alter (list)->base
.
list | List to operate on |
item | Item to append |