From 1ff568a91b88652d1a12165607f5230e71e210e6 Mon Sep 17 00:00:00 2001 From: cloudwalk Date: Sun, 8 Nov 2020 06:25:12 +0000 Subject: [PATCH] com_list: Implement the rest of the linked list API from the Linux kernel Also added DP_GCC_COMPATIBLE define. Used to make checking for GCC-compatible compilers easier (for checking if we should use typeof or decltype in this case). git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@13037 d7cf8633-e32d-0410-b094-e92efae38249 --- cmd.c | 28 ++++----- com_list.c | 69 ++++++++++++-------- com_list.h | 182 ++++++++++++++++++++++++++++++++++++++++++++++++++--- qdefs.h | 10 +++ world.c | 12 ++-- 5 files changed, 248 insertions(+), 53 deletions(-) diff --git a/cmd.c b/cmd.c index c353b4c8..47d5825c 100644 --- a/cmd.c +++ b/cmd.c @@ -86,21 +86,21 @@ static void Cmd_Defer_f (cmd_state_t *cmd) if(Cmd_Argc(cmd) == 1) { - if(List_IsEmpty(&cbuf->deferred)) + if(List_Is_Empty(&cbuf->deferred)) Con_Printf("No commands are pending.\n"); else { llist_t *pos; - List_ForEach(pos, &cbuf->deferred) + List_For_Each(pos, &cbuf->deferred) { - current = List_Container(*pos, cmd_input_t, list); + current = List_Entry(*pos, cmd_input_t, list); Con_Printf("-> In %9.2f: %s\n", current->delay, current->text); } } } else if(Cmd_Argc(cmd) == 2 && !strcasecmp("clear", Cmd_Argv(cmd, 1))) { - while(!List_IsEmpty(&cbuf->deferred)) + while(!List_Is_Empty(&cbuf->deferred)) List_Move_Tail(cbuf->deferred.next, &cbuf->free); } else if(Cmd_Argc(cmd) == 3) @@ -185,9 +185,9 @@ static cmd_input_t *Cbuf_LinkGet(cmd_buf_t *cbuf, cmd_input_t *existing) cmd_input_t *ret = NULL; if(existing && existing->pending) ret = existing; - else if(!List_IsEmpty(&cbuf->free)) + else if(!List_Is_Empty(&cbuf->free)) { - ret = List_Container(*cbuf->free.next, cmd_input_t, list); + ret = List_Entry(*cbuf->free.next, cmd_input_t, list); ret->length = 0; ret->pending = false; } @@ -361,8 +361,8 @@ void Cbuf_AddText (cmd_state_t *cmd, const char *text) Con_Print("Cbuf_AddText: overflow\n"); else { - Cbuf_LinkCreate(cmd, &llist, (List_IsEmpty(&cbuf->start) ? NULL : List_Container(*cbuf->start.prev, cmd_input_t, list)), text); - if(!List_IsEmpty(&llist)) + Cbuf_LinkCreate(cmd, &llist, (List_Is_Empty(&cbuf->start) ? NULL : List_Entry(*cbuf->start.prev, cmd_input_t, list)), text); + if(!List_Is_Empty(&llist)) List_Splice_Tail(&llist, &cbuf->start); } Cbuf_Unlock(cbuf); @@ -389,8 +389,8 @@ void Cbuf_InsertText (cmd_state_t *cmd, const char *text) Con_Print("Cbuf_InsertText: overflow\n"); else { - Cbuf_LinkCreate(cmd, &llist, List_Container(*cbuf->start.next, cmd_input_t, list), text); - if(!List_IsEmpty(&llist)) + Cbuf_LinkCreate(cmd, &llist, List_Entry(*cbuf->start.next, cmd_input_t, list), text); + if(!List_Is_Empty(&llist)) List_Splice(&llist, &cbuf->start); } @@ -415,9 +415,9 @@ static void Cbuf_Execute_Deferred (cmd_buf_t *cbuf) return; cbuf->deferred_oldtime = host.realtime; - List_ForEach(pos, &cbuf->deferred) + List_For_Each(pos, &cbuf->deferred) { - current = List_Container(*pos, cmd_input_t, list); + current = List_Entry(*pos, cmd_input_t, list); current->delay -= eat; if(current->delay <= 0) { @@ -444,14 +444,14 @@ void Cbuf_Execute (cmd_buf_t *cbuf) // LadyHavoc: making sure the tokenizebuffer doesn't get filled up by repeated crashes cbuf->tokenizebufferpos = 0; - while (!List_IsEmpty(&cbuf->start)) + while (!List_Is_Empty(&cbuf->start)) { /* * Delete the text from the command buffer and move remaining * commands down. This is necessary because commands (exec, alias) * can insert data at the beginning of the text buffer */ - current = List_Container(*cbuf->start.next, cmd_input_t, list); + current = List_Entry(*cbuf->start.next, cmd_input_t, list); // Recycle memory so using WASD doesn't cause a malloc and free List_Move_Tail(¤t->list, &cbuf->free); diff --git a/com_list.c b/com_list.c index 5f8d178d..2281c474 100644 --- a/com_list.c +++ b/com_list.c @@ -18,17 +18,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -// com_list.c - generic doubly linked list interface, inspired by Linux list.h +// com_list.c - generic doubly linked list interface, adapted from Linux list.h #include "qtypes.h" #include "com_list.h" +/* + * Creates a new linked list. Initializes the head to point to itself. + * If it's a list header, the result is an empty list. + */ +inline void List_Create(llist_t *list) +{ + list->next = list->prev = NULL; +} + /* * Insert a node between two known nodes. * * Only use when prev and next are known. */ -static void __List_Add(llist_t *node, llist_t *prev, llist_t *next) +static inline void __List_Add(llist_t *node, llist_t *prev, llist_t *next) { next->prev = node; node->next = next; @@ -39,7 +48,7 @@ static void __List_Add(llist_t *node, llist_t *prev, llist_t *next) /* * Insert a node immediately after head. */ -void List_Add(llist_t *node, llist_t *head) +inline void List_Add(llist_t *node, llist_t *head) { __List_Add(node, head, head->next); } @@ -47,7 +56,7 @@ void List_Add(llist_t *node, llist_t *head) /* * Insert a node immediately before head. */ -void List_Add_Tail(llist_t *node, llist_t *head) +inline void List_Add_Tail(llist_t *node, llist_t *head) { __List_Add(node, head->prev, head); } @@ -55,7 +64,7 @@ void List_Add_Tail(llist_t *node, llist_t *head) /* * Bridge prev and next together, when removing the parent of them. */ -static void __List_Delete(llist_t *prev, llist_t *next) +static inline void __List_Delete(llist_t *prev, llist_t *next) { next->prev = prev; prev->next = next; @@ -64,7 +73,7 @@ static void __List_Delete(llist_t *prev, llist_t *next) /* * Redundant? */ -static void __List_Delete_Node(llist_t *node) +static inline void __List_Delete_Node(llist_t *node) { __List_Delete(node->prev, node->next); } @@ -72,7 +81,7 @@ static void __List_Delete_Node(llist_t *node) /* * Removes a node from its list. Sets its pointers to NULL. */ -void List_Delete(llist_t *node) +inline void List_Delete(llist_t *node) { __List_Delete_Node(node); node->next = node->prev = NULL; @@ -81,7 +90,7 @@ void List_Delete(llist_t *node) /* * Removes a node from its list. Reinitialize it. */ -void List_Delete_Init(llist_t *node) +inline void List_Delete_Init(llist_t *node) { __List_Delete_Node(node); node->next = node->prev = node; @@ -90,7 +99,7 @@ void List_Delete_Init(llist_t *node) /* * Replace old with new. Old is overwritten if empty. */ -void List_Replace(llist_t *old, llist_t *_new) +inline void List_Replace(llist_t *old, llist_t *_new) { _new->next = old->next; _new->next->prev = _new; @@ -99,10 +108,20 @@ void List_Replace(llist_t *old, llist_t *_new) old->next = old->prev = old; } +/* + * Replace old with new. Initialize old. + * Old is overwritten if empty. + */ +inline void List_Replace_Init(llist_t *old, llist_t *_new) +{ + List_Replace(old, _new); + List_Create(old); +} + /* * Swap node1 with node2 in place. */ -void List_Swap(llist_t *node1, llist_t *node2) +inline void List_Swap(llist_t *node1, llist_t *node2) { llist_t *pos = node2->prev; List_Delete_Init(node2); @@ -115,7 +134,7 @@ void List_Swap(llist_t *node1, llist_t *node2) /* * Delete list from its... list, then insert after head. */ -void List_Move(llist_t *list, llist_t *head) +inline void List_Move(llist_t *list, llist_t *head) { __List_Delete_Node(list); List_Add(list, head); @@ -124,7 +143,7 @@ void List_Move(llist_t *list, llist_t *head) /* * Delete list from its... list, then insert before head. */ -void List_Move_Tail(llist_t *list, llist_t *head) +inline void List_Move_Tail(llist_t *list, llist_t *head) { __List_Delete_Node(list); List_Add_Tail(list, head); @@ -135,7 +154,7 @@ void List_Move_Tail(llist_t *list, llist_t *head) * All three parameters must belong to the same list. */ -void List_Bulk_Move_Tail(llist_t *head, llist_t *first, llist_t *last) +inline void List_Bulk_Move_Tail(llist_t *head, llist_t *first, llist_t *last) { first->prev->next = last->next; last->next->prev = first->prev; @@ -151,11 +170,11 @@ void List_Bulk_Move_Tail(llist_t *head, llist_t *first, llist_t *last) * Shift the head to the right (like rotating a wheel counterclockwise). * The node immediately to the right becomes the new head. */ -void List_Rotate_Left(llist_t *head) +inline void List_Rotate_Left(llist_t *head) { llist_t *first; - if (!List_IsEmpty(head)) + if (!List_Is_Empty(head)) { first = head->next; List_Move_Tail(first, head); @@ -165,7 +184,7 @@ void List_Rotate_Left(llist_t *head) /* * Make list the new head. */ -void List_Rotate_To_Front(llist_t *list, llist_t *head) +inline void List_Rotate_To_Front(llist_t *list, llist_t *head) { List_Move_Tail(head, list); } @@ -173,7 +192,7 @@ void List_Rotate_To_Front(llist_t *list, llist_t *head) /* * Concatenate two lists. The head of list will be discarded. */ -static void __List_Splice(const llist_t *list, llist_t *prev, llist_t *next) +static inline void __List_Splice(const llist_t *list, llist_t *prev, llist_t *next) { llist_t *first = list->next; llist_t *last = list->prev; @@ -188,32 +207,32 @@ static void __List_Splice(const llist_t *list, llist_t *prev, llist_t *next) /* * Concatenate two lists. The first node of list will be inserted after head. */ -void List_Splice(const llist_t *list, llist_t *head) +inline void List_Splice(const llist_t *list, llist_t *head) { - if(!List_IsEmpty(list)) + if(!List_Is_Empty(list)) __List_Splice(list, head, head->next); } /* * Concatenate two lists. The tail of list will be inserted before head. */ -void List_Splice_Tail(const llist_t *list, llist_t *head) +inline void List_Splice_Tail(const llist_t *list, llist_t *head) { - if (!List_IsEmpty(list)) + if (!List_Is_Empty(list)) __List_Splice(list, head->prev, head); } -qbool List_IsFirst(llist_t *list, llist_t *start) +inline qbool List_Is_First(llist_t *list, llist_t *start) { return list->prev == start; } -qbool List_IsLast(llist_t *list, llist_t *start) +inline qbool List_Is_Last(llist_t *list, llist_t *start) { return list->next == start; } -qbool List_IsEmpty(const llist_t *list) +inline qbool List_Is_Empty(const llist_t *list) { return list->next == list; -} \ No newline at end of file +} diff --git a/com_list.h b/com_list.h index e99ee346..9fe42a31 100644 --- a/com_list.h +++ b/com_list.h @@ -18,13 +18,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -// com_list.c - generic doubly linked list interface, inspired by Linux list.h +// com_list.h - generic doubly linked list interface, adapted from Linux list.h #ifndef LIST_H #define LIST_H #include #include "qtypes.h" +#include "qdefs.h" typedef struct llist_s { @@ -32,21 +33,186 @@ typedef struct llist_s struct llist_s *next; } llist_t; -#define List_Head_Reset(name) { &(name), &(name) } +#define LIST_HEAD_INIT(name) { &(name), &(name) } -#define List_Container(ptr, type, member) ContainerOf(ptr, type, member) +#define LIST_HEAD(name) \ + struct llist_s name = LIST_HEAD_INIT(name) -#define List_ForEach(pos, head) \ +/* + * Get the struct for this entry + */ +#define List_Entry(ptr, type, member) ContainerOf(ptr, type, member) + +/* + * Get the first element from a list + * The list is expected to not be empty + */ +#define List_First_Entry(ptr, type, member) List_Entry((ptr)->next, type, member) + +/* + * Get the last element from the list + * The list is expected to not be empty + */ +#define List_Last_Entry(ptr, type, member) List_Entry((ptr)->prev, type, member) + +/* + * Get the first element from the list, but return NULL if it's empty + */ +#define List_First_Entry_Or_Null(ptr, type, member) ({ \ + struct llist_s *head__ = (ptr); \ + struct llist_s *pos__ = head__->next; \ + pos__ != head__ ? List_Entry(pos__, type, member) : NULL; \ +}) + +/* + * Get the next element in the list + */ +#define List_Next_Entry(pos, member) \ + List_Entry((pos)->member.next, Q_typeof(*(pos)), member) + +/* + * Get the prev element in the list + */ +#define List_Prev_Entry(pos, member) \ + List_Entry((pos)->member.prev, Q_typeof(*(pos)), member) + +/* + * Iterate over a list + */ +#define List_For_Each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) -#define List_ForEach_Prev(pos, head) \ +/* + * Continue iteration over a list, after the current position + */ +#define List_For_Each_Continue(pos, head) \ + for (pos = pos->next; pos != (head); pos = pos->next) + +/* + * Iterate over a list backwards + */ +#define List_For_Each_Prev(pos, head) \ for (pos = (head)->prev; pos != (head); pos = pos->prev) +/* + * Iterate over a list, safe against removal of list entry + */ +#define List_For_Each_Safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) +/* + * Iterate over a list backwards, safe against removal of list entry + */ +#define List_For_Each_Prev_Safe(pos, n, head) \ + for (pos = (head)->prev, n = pos->prev; \ + pos != (head); \ + pos = n, n = pos->prev) +/* + * Test if the entry points to the head of the list + */ +#define List_Entry_Is_Head(pos, head, member) \ + (&pos->member == (head)) + +/* + * Iterate over a list of a given type + */ +#define List_For_Each_Entry(pos, head, member) \ + for (pos = List_Last_Entry(head, Q_typeof(*pos), member); \ + !List_Entry_Is_Head(pos, head, member); \ + pos = List_Prev_Entry(pos, member)) + +/* + * Iterate over a list of a given type backwards + */ +#define List_For_Each_Prev_Entry(pos, head, member) \ + for (pos = List_Last_Entry(head, Q_typeof(*pos), member); \ + !List_Entry_Is_Head(pos, head, member); \ + pos = List_Prev_Entry(pos, member)) + +/* + * Prepares a pos entry for use as a start point in List_For_Each_Entry_Continue() + */ +#define List_Prepare_Entry(pos, head, member) \ + ((pos) ? : List_Entry(head, Q_typeof(*pos), member)) + +/* + * Continue iteration over a list of a given type, after the current position + */ +#define List_For_Each_Entry_Continue(pos, head, member) \ + for (pos = List_Next_Entry(pos, member); \ + !List_Entry_Is_Head(pos, head, member); \ + pos = List_Next_Entry(pos, member)) + +/* + * Continue iteration over a list of a given type backwards, after the current position + */ +#define List_For_Each_Prev_Entry_Continue(pos, head, member) \ + for (pos = List_Prev_Entry(pos, member); \ + !List_Entry_Is_Head(pos, head, member); \ + pos = List_Prev_Entry(pos, member)) + +/* + * Continue iteration over a list of a given type, from the current position + */ +#define List_For_Each_Entry_From(pos, head, member) \ + for (; !List_Entry_Is_Head(pos, head, member); \ + pos = List_Next_Entry(pos, member)) + +/* + * Continue iteration over a list of a given type backwards, from the current position + */ +#define List_For_Each_Prev_Entry_From(pos, head, member) \ + for (; !List_Entry_Is_Head(pos, head, member); \ + pos = List_Prev_Entry(pos, member)) + +/* + * Iterate over a list of a given type, safe against removal of list entry + */ +#define List_For_Each_Entry_Safe(pos, n, head, member) \ + for (pos = List_First_Entry(head, Q_typeof(*pos), member), \ + n = List_Next_Entry(pos, member); \ + !List_Entry_Is_Head(pos, head, member); \ + pos = n, n = List_Next_Entry(n, member)) +/* + * Continue iteration over a list of a given type, after the current position, safe against removal of list entry + */ +#define List_For_Each_Entry_Safe_Continue(pos, n, head, member) \ + for (pos = List_Next_Entry(pos, member), \ + n = List_Next_Entry(pos, member); \ + !List_Entry_Is_Head(pos, head, member); \ + pos = n, n = List_Next_Entry(n, member)) + +/* + * Continue iteration over a list of a given type, from the current position, safe against removal of list entry + */ +#define List_For_Each_Entry_Safe_From(pos, n, head, member) \ + for (n = List_Next_Entry(pos, member); \ + !List_Entry_Is_Head(pos, head, member); \ + pos = n, n = List_Next_Entry(n, member)) + + +/* + * Iterate over a list of a given type backwards, safe against removal of list entry + */ +#define List_For_Each_Prev_Entry_Safe(pos, n, head, member) \ + for (pos = List_Last_Entry(head, Q_typeof(*pos), member), \ + n = List_Prev_Entry(pos, member); \ + !List_Entry_Is_Head(pos, head, member); \ + pos = n, n = List_Prev_Entry(n, member)) + +/* + * Reset a stale List_For_Each_Entry_Safe loop + */ +#define List_Safe_Reset_Next(pos, n, member) \ + n = List_Next_Entry(pos, member) + +void List_Create(llist_t *list); void List_Add(llist_t *node, llist_t *start); void List_Add_Tail(llist_t *node, llist_t *start); void List_Delete(llist_t *node); void List_Delete_Init(llist_t *node); void List_Replace(llist_t *old, llist_t *_new); +void List_Replace_Init(llist_t *old, llist_t *_new); void List_Swap(llist_t *node1, llist_t *node2); void List_Move(llist_t *list, llist_t *start); void List_Move_Tail(llist_t *list, llist_t *start); @@ -55,8 +221,8 @@ void List_Rotate_Left(llist_t *head); void List_Rotate_To_Front(llist_t *list, llist_t *head); void List_Splice(const llist_t *list, llist_t *head); void List_Splice_Tail(const llist_t *list, llist_t *head); -qbool List_IsFirst(llist_t *list, llist_t *start); -qbool List_IsLast(llist_t *list, llist_t *start); -qbool List_IsEmpty(const llist_t *list); +qbool List_Is_First(llist_t *list, llist_t *start); +qbool List_Is_Last(llist_t *list, llist_t *start); +qbool List_Is_Empty(const llist_t *list); #endif diff --git a/qdefs.h b/qdefs.h index 4421ce1d..463761e9 100644 --- a/qdefs.h +++ b/qdefs.h @@ -1,6 +1,10 @@ #ifndef QDEFS_H #define QDEFS_H +#if defined (__GNUC__) || defined (__clang__) || defined (__TINYC__) +#define DP_GCC_COMPATIBLE +#endif + #if (__GNUC__ > 2) || defined (__clang__) || (__TINYC__) #define DP_FUNC_PRINTF(n) __attribute__ ((format (printf, n, n+1))) #define DP_FUNC_PURE __attribute__ ((pure)) @@ -11,6 +15,12 @@ #define DP_FUNC_NORETURN #endif +#ifdef DP_GCC_COMPATIBLE +#define Q_typeof(var) typeof(var) +#elif defined (MSVC) +#define Q_typeof(var) decltype(var) +#endif + #define MAX_NUM_ARGVS 50 #ifdef DP_SMALLMEMORY diff --git a/world.c b/world.c index d7369692..9a20fd72 100644 --- a/world.c +++ b/world.c @@ -147,10 +147,10 @@ void World_UnlinkAll(world_t *world) // unlink all entities one by one grid = &world->areagrid_outside; while (grid->list.next != &grid->list) - World_UnlinkEdict(PRVM_EDICT_NUM(List_Container(*grid->list.next, link_t, list)->entitynumber)); + World_UnlinkEdict(PRVM_EDICT_NUM(List_Entry(*grid->list.next, link_t, list)->entitynumber)); for (i = 0, grid = world->areagrid;i < AREA_GRIDNODES;i++, grid++) while (grid->list.next != &grid->list) - World_UnlinkEdict(PRVM_EDICT_NUM(List_Container(*grid->list.next, link_t, list)->entitynumber)); + World_UnlinkEdict(PRVM_EDICT_NUM(List_Entry(*grid->list.next, link_t, list)->entitynumber)); } /* @@ -215,9 +215,9 @@ int World_EntitiesInBox(world_t *world, const vec3_t requestmins, const vec3_t r if (world->areagrid_outside.list.next) { grid = &world->areagrid_outside; - List_ForEach(pos, &grid->list) + List_For_Each(pos, &grid->list) { - l = List_Container(*pos, link_t, list); + l = List_Entry(*pos, link_t, list); ent = PRVM_EDICT_NUM(l->entitynumber); if (ent->priv.server->areagridmarknumber != world->areagrid_marknumber) { @@ -240,9 +240,9 @@ int World_EntitiesInBox(world_t *world, const vec3_t requestmins, const vec3_t r { if (grid->list.next) { - List_ForEach(pos, &grid->list) + List_For_Each(pos, &grid->list) { - l = List_Container(*pos, link_t, list); + l = List_Entry(*pos, link_t, list); ent = PRVM_EDICT_NUM(l->entitynumber); if (ent->priv.server->areagridmarknumber != world->areagrid_marknumber) { -- 2.39.2