1 /*
2
3 Copyright (C) 2015-2018 Night Dive Studios, LLC.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 */
19 // LList.C Doubly-linked list
20 // Rex E. Bradford (REX)
21 //
22 // This module, along with macros in llist.h and routines in
23 // lllist.c, implements a "managed-storage" linked list of
24 // homogenous (same-sized) nodes. The llist module takes care
25 // of allocation and deallocation of storage for nodes, using
26 // Malloc() to allocate several nodes at a time (the amount is
27 // user-specifiable on a per-list basis). The storage is
28 // dynamic; a list may grow without bound until Malloc() fails.
29 // Sorted lists, or queues, are handled by this module as well.
30 //
31 // See llist.h for the full interface, and llist.txt for documentation.
32 /*
33 * $Header: n:/project/lib/src/dstruct/RCS/llist.c 1.2 1993/04/16 12:01:57 rex Exp $
34 * $log$
35 */
36
37 #include <stdlib.h>
38 #include <string.h>
39 #include "lg.h"
40 #include "llist.h"
41 //#include "memall.h"
42
43 void LlistGrowList(LlistHead *plh);
44 void LlistInitNodeBlock(LlistHead *plh, llist *pNodeBlock, ushort blockSize);
45
46 // ---------------------------------------------------------
47 // LLIST ROUTINES
48 // ---------------------------------------------------------
49 //
50 // LlistInit() initializes a list.
51 //
52 // plh = ptr to list header
53 // nodeSize = size of each list node, including embedded _llist
54 // numNodesPerBlock = # nodes to allocate in each Malloc() block
55
LlistInit(LlistHead * plh,ushort nodeSize,short numNodesPerBlock)56 void LlistInit(LlistHead *plh, ushort nodeSize, short numNodesPerBlock)
57 {
58 // Initialize basic part of linked list header
59
60 llist_init(plh);
61
62 // Set allocation params
63
64 plh->nodeSize = nodeSize;
65 plh->numNodesPerBlock = numNodesPerBlock;
66
67 // Allocate initial list block
68
69 plh->pNodeStore = NULL;
70 LlistGrowList(plh); // plh->pfree initialized by this call
71 }
72
73 // --------------------------------------------------------
74 //
75 // LlistAddHead() adds first free node to head of list.
76 //
77 // plh = ptr to list header
78 //
79 // returns: ptr to list node, with pnext & pprev already filled in
80
LlistAddHead(LlistHead * plh)81 void *LlistAddHead(LlistHead *plh)
82 {
83 llist *pll;
84
85 // Check for room
86
87 if (plh->pfree == NULL)
88 LlistGrowList(plh);
89
90 // Get next free node off free list
91
92 pll = plh->pfree;
93 plh->pfree = pll->pnext;
94
95 // Insert at head of list
96
97 llist_add_head(plh, pll);
98
99 // Return ptr to element
100
101 return(pll);
102 }
103
104 // --------------------------------------------------------
105 //
106 // LlistAddTail() adds first free node to tail of list.
107 //
108 // plh = ptr to list header
109 //
110 // returns: ptr to list node, with pnext & pprev already filled in
111
LlistAddTail(LlistHead * plh)112 void *LlistAddTail(LlistHead *plh)
113 {
114 llist *pll;
115
116 // Check for room
117
118 if (plh->pfree == NULL)
119 LlistGrowList(plh);
120
121 // Get next free node off free list
122
123 pll = plh->pfree;
124 plh->pfree = pll->pnext;
125
126 // Insert at tail of list
127
128 llist_add_tail(plh, pll);
129
130 // Return ptr to element
131
132 return(pll);
133 }
134
135 // --------------------------------------------------------
136 //
137 // LlistAddQueue() adds first free element in priority order.
138 //
139 // plh = queue header
140 // prior = priority value for new node
141 //
142 // returns: ptr to list node, with pnext,pprev,prior already filled in
143
LlistAddQueue(LlistHead * plh,short prior)144 void *LlistAddQueue(LlistHead *plh, short prior)
145 {
146 queue *plq;
147
148 // Check for room
149
150 if (plh->pfree == NULL)
151 LlistGrowList(plh);
152
153 // Get next free node off free list
154
155 plq = (queue *) plh->pfree;
156 plh->pfree = (llist *) plq->pnext;
157
158 // Insert in priority order
159
160 plq->priority = prior;
161 llist_insert_queue((llist_head *) plh, plq);
162
163 // Return ptr to item
164
165 return(plq);
166 }
167
168 // --------------------------------------------------------
169 //
170 // LlistMoveQueue() sets new queue nodes priority, moves.
171 //
172 // plh = ptr to queue header
173 // pnode = ptr to node to be moved
174 // newprior = new priority value
175 //
176 // returns: TRUE if item was actually moved in list, false if newprior
177 // didn't cause it to switch places with other nodes.
178
LlistMoveQueue(LlistHead * plh,void * pnode,short newprior)179 uchar LlistMoveQueue(LlistHead *plh, void *pnode, short newprior)
180 {
181 ((queue *) pnode)->priority = newprior;
182 return(llist_move_queue((llist_head *)plh, (queue *)pnode));
183 }
184
185 // --------------------------------------------------------
186 //
187 // LlistFree() frees a list element.
188 //
189 // plh = ptr to list or queue header
190 // pnode = ptr to list node to free
191
LlistFree(LlistHead * plh,void * pnode)192 void LlistFree(LlistHead *plh, void *pnode)
193 {
194 // Remove from list
195
196 llist_remove((llist *) pnode);
197
198 // Return node to head of free list
199
200 ((llist *)pnode)->pnext = plh->pfree;
201 plh->pfree = (llist *)pnode;
202 }
203
204 // --------------------------------------------------------
205 //
206 // LlistFreeAll() frees all elements in list.
207 // Note: this does not reclaim list memory, use ListDestroy()
208 //
209 // plh = ptr to list or queue header
210
LlistFreeAll(LlistHead * plh)211 void LlistFreeAll(LlistHead *plh)
212 {
213 llist *pnb;
214 ushort blockSize;
215
216 // Init head & tail ptrs, zero num nodes
217
218 plh->head.pnext = LlistEnd(plh);
219 plh->tail.pprev = LlistBeg(plh);
220
221 // Reset free list of nodes to span across all storage blocks
222
223 blockSize = plh->nodeSize * plh->numNodesPerBlock;
224 for (pnb = plh->pNodeStore; pnb != NULL; pnb = pnb->pnext)
225 LlistInitNodeBlock(plh, pnb, blockSize);
226 }
227
228 // --------------------------------------------------------
229 //
230 // LlistDestroy() destroys a list.
231 //
232 // plh = ptr to list or queue header
233 // --------------------------------------------------------
234 // For Mac version: Use free instead of Free.
235
LlistDestroy(LlistHead * plh)236 void LlistDestroy(LlistHead *plh)
237 {
238 llist *pnb;
239 llist *pnbNext;
240
241 // Free all storage blocks
242
243 pnb = plh->pNodeStore;
244 while (pnb)
245 {
246 pnbNext = pnb->pnext;
247 free(pnb);
248 pnb = pnbNext;
249 }
250
251 // Reinitialize list header (must re-init to use again!)
252
253 LG_memset(plh, 0, sizeof(LlistHead));
254 }
255
256 // --------------------------------------------------------
257 // PRIVATE ROUTINES
258 // --------------------------------------------------------
259 //
260 // LlistGrowList() grows a linked list. It adds a new
261 // storage block (the number of nodes per block is set
262 // when LlistInit() is called).
263 //
264 // plh = ptr to list or queue header
265 // --------------------------------------------------------
266 // For Mac version: Use malloc instead of Malloc.
267
LlistGrowList(LlistHead * plh)268 void LlistGrowList(LlistHead *plh)
269 {
270 ushort blockSize;
271 llist *pNewStore;
272
273 // Allocate new storage block
274
275 blockSize = plh->nodeSize * plh->numNodesPerBlock;
276 pNewStore = (llist *)malloc(sizeof(llist) + blockSize);
277
278 // Link it in to the node store list
279
280 pNewStore->pnext = plh->pNodeStore;
281 plh->pNodeStore = pNewStore;
282
283 // Build the free list chain
284
285 LlistInitNodeBlock(plh, pNewStore, blockSize);
286 }
287
288 // ---------------------------------------------------------
289 //
290 // LlistInitNodeBlock() initializes a node block.
291 //
292 // plh = ptr to list or queue header
293 // pNodeBlock = ptr to node block to initialize
294 // blockSize = size of the block
295
LlistInitNodeBlock(LlistHead * plh,llist * pNodeBlock,ushort blockSize)296 void LlistInitNodeBlock(LlistHead *plh, llist *pNodeBlock, ushort blockSize)
297 {
298 llist *pll;
299
300 plh->pfree = pNodeBlock + 1; // point free to just past links
301 for (pll = plh->pfree;
302 pll < (llist *) (((char *) plh->pfree) + (blockSize - plh->nodeSize));
303 pll = (llist *) (((char *) pll) + plh->nodeSize))
304 pll->pnext = (llist *) (((char *) pll) + plh->nodeSize);
305 pll->pnext = NULL;
306 }
307