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