1 /**
2  * @file
3  * @brief LINKED LIST implementation
4  */
5 
6 /*
7 Copyright (C) 2002-2013 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23 
24 */
25 
26 #include "list.h"
27 #include "common.h"
28 #include "mem.h"
29 #include <assert.h>
30 #include <string.h>
31 
LIST_AllocateEntry(void * const data,linkedList_t * const next=0)32 static linkedList_t* LIST_AllocateEntry(void* const data, linkedList_t* const next = 0)
33 {
34 	linkedList_t* const e = Mem_PoolAllocType(linkedList_t, com_genericPool);
35 	e->data = data;
36 	e->next = next;
37 	return e;
38 }
39 
40 /** Append entry to end of list. */
LIST_AppendEntry(linkedList_t ** list,linkedList_t * const entry)41 static void LIST_AppendEntry(linkedList_t** list, linkedList_t* const entry)
42 {
43 	while (*list) list = &(*list)->next;
44 	*list = entry;
45 }
46 
47 /**
48  * @brief Adds an entry to a new or to an already existing linked list
49  * @sa LIST_AddString
50  * @sa LIST_RemoveEntry
51  * @return Returns a pointer to the data that has been added, wrapped in a linkedList_t
52  * @todo Optimize this to not allocate memory for every entry - but use a hunk
53  */
LIST_Add(linkedList_t ** listDest,void const * data,size_t length)54 linkedList_t* LIST_Add (linkedList_t** listDest, void const* data, size_t length)
55 {
56 	assert(listDest);
57 	assert(data);
58 
59 	void*         const dataCopy = memcpy(Mem_PoolAllocTypeN(byte, length, com_genericPool), data, length);
60 	linkedList_t* const newEntry = LIST_AllocateEntry(dataCopy);
61 
62 	LIST_AppendEntry(listDest, newEntry);
63 
64 	return newEntry;
65 }
66 
67 /**
68  * @brief Searches for the first occurrence of a given string
69  * @return the linkedList_t pointer if the string is found, otherwise @c nullptr
70  * @note if string is @c nullptr, the function returns @c nullptr
71  * @sa LIST_AddString
72  */
LIST_ContainsString(const linkedList_t * list,const char * string)73 const linkedList_t* LIST_ContainsString (const linkedList_t* list, const char* string)
74 {
75 	while ((string != nullptr) && (list != nullptr)) {
76 		if (Q_streq(static_cast<char const*>(list->data), string))
77 			return list;
78 		list = list->next;
79 	}
80 
81 	return nullptr;
82 }
83 
84 /**
85  * @brief Searches for the first occurrence of a given pointer
86  * @return the linkedList_t pointer if the string is found, otherwise @c nullptr
87  * @note if data is @c nullptr, the function returns @c nullptr
88  * @note O(n)
89  * @note Only use this for small linked lists
90  */
LIST_GetPointer(linkedList_t * list,const void * data)91 linkedList_t* LIST_GetPointer (linkedList_t* list, const void* data)
92 {
93 	while ((data != nullptr) && (list != nullptr)) {
94 		if (list->data == data)
95 			return list;
96 		list = list->next;
97 	}
98 
99 	return nullptr;
100 }
101 
LIST_AllocateString(char const * data,linkedList_t * const next=0)102 static linkedList_t* LIST_AllocateString(char const* data, linkedList_t* const next = 0)
103 {
104 	return LIST_AllocateEntry(Mem_StrDup(data), next);
105 }
106 
LIST_AddStringSorted(linkedList_t ** listDest,const char * data)107 void LIST_AddStringSorted (linkedList_t** listDest, const char* data)
108 {
109 	assert(listDest);
110 	assert(data);
111 
112 	for (; *listDest; listDest = &(*listDest)->next) {
113 		if (Q_StringSort(data, static_cast<char const*>((*listDest)->data)) < 0)
114 			break;
115 	}
116 
117 	*listDest = LIST_AllocateString(data, *listDest);
118 }
119 
120 /**
121  * @brief Adds a string as first entry to a linked list
122  * @param listDest The linked list to add the string, too. If this is @c nullptr, a new list is created
123  * @param data The string to add to the list
124  * @sa LIST_AddString
125  * @todo Optimize this to not allocate memory for every entry - but use a hunk
126  */
LIST_PrependString(linkedList_t ** listDest,const char * data)127 void LIST_PrependString (linkedList_t** listDest, const char* data)
128 {
129 	*listDest = LIST_AllocateString(data, *listDest);
130 }
131 
132 /**
133  * @brief Adds an string to a new or to an already existing linked list. The string is copied here.
134  * @sa LIST_Add
135  * @sa LIST_RemoveEntry
136  * @see LIST_PrependString
137  * @todo Optimize this to not allocate memory for every entry - but use a hunk
138  */
LIST_AddString(linkedList_t ** listDest,const char * data)139 void LIST_AddString (linkedList_t** listDest, const char* data)
140 {
141 	assert(listDest);
142 	assert(data);
143 
144 	LIST_AppendEntry(listDest, LIST_AllocateString(data));
145 }
146 
147 /**
148  * @brief Adds just a pointer to a new or to an already existing linked list
149  * @sa LIST_Add
150  * @sa LIST_RemoveEntry
151  * @todo Optimize this to not allocate memory for every entry - but use a hunk
152  */
LIST_AddPointer(linkedList_t ** listDest,void * data)153 void LIST_AddPointer (linkedList_t** listDest, void* data)
154 {
155 	assert(listDest);
156 	assert(data);
157 
158 	linkedList_t* const newEntry = LIST_AllocateEntry(data);
159 	newEntry->ptr = true;
160 
161 	LIST_AppendEntry(listDest, newEntry);
162 }
163 
164 /**
165  * @brief Removes one entry from the linked list
166  * @sa LIST_AddString
167  * @sa LIST_Add
168  * @sa LIST_AddPointer
169  * @sa LIST_Delete
170  * @return @c true if the removal was successful, @c false otherwise.
171  */
LIST_RemoveEntry(linkedList_t ** list,linkedList_t * entry)172 bool LIST_RemoveEntry (linkedList_t** list, linkedList_t* entry)
173 {
174 	assert(list);
175 	assert(entry);
176 
177 	for (; *list; list = &(*list)->next) {
178 		if (*list == entry) {
179 			/* Unlink the entry. */
180 			*list = entry->next;
181 			/* Delete the entry. */
182 			if (!entry->ptr) Mem_Free(entry->data);
183 			Mem_Free(entry);
184 			return true;
185 		}
186 	}
187 
188 	return false;
189 }
190 
191 /**
192  * @sa LIST_Add
193  * @sa LIST_RemoveEntry
194  */
LIST_Delete(linkedList_t ** list)195 void LIST_Delete (linkedList_t** list)
196 {
197 	linkedList_t* next;
198 	linkedList_t* l = *list;
199 
200 	while (l) {
201 		next = l->next;
202 		if (!l->ptr)
203 			Mem_Free(l->data);
204 		Mem_Free(l);
205 		l = next;
206 	}
207 	*list = nullptr;
208 }
209 
210 /**
211  * @sa LIST_Add
212  * @sa LIST_RemoveEntry
213  */
LIST_Remove(linkedList_t ** list,const void * data)214 bool LIST_Remove (linkedList_t** list, const void* data)
215 {
216 	linkedList_t* l = LIST_GetPointer(*list, data);
217 	if (l != nullptr)
218 		return LIST_RemoveEntry(list, l);
219 	return false;
220 }
221 
222 /**
223  * @brief This is the actual sort function. Notice that it returns the new
224  * head of the list. (It has to, because the head will not
225  * generally be the same element after the sort.)
226  * @note see http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
227  */
_LIST_Sort(linkedList_t * list,linkedListSort_t sorter,const void * userData)228 static linkedList_t* _LIST_Sort (linkedList_t* list, linkedListSort_t sorter, const void* userData)
229 {
230 	linkedList_t* p, *q, *e;
231 	int insize, psize, qsize, i;
232 
233 	/*
234 	 * Silly special case: if `list' was passed in as nullptr, return
235 	 * nullptr immediately.
236 	 */
237 	if (!list)
238 		return nullptr;
239 
240 	insize = 1;
241 
242 	while (1) {
243 		p = list;
244 		list = nullptr;
245 		linkedList_t* tail = nullptr;
246 
247 		int nmerges = 0; /* count number of merges we do in this pass */
248 
249 		while (p) {
250 			nmerges++; /* there exists a merge to be done */
251 			/* step `insize' places along from p */
252 			q = p;
253 			psize = 0;
254 			for (i = 0; i < insize; i++) {
255 				psize++;
256 				q = q->next;
257 				if (!q)
258 					break;
259 			}
260 
261 			/* if q hasn't fallen off end, we have two lists to merge */
262 			qsize = insize;
263 
264 			/* now we have two lists; merge them */
265 			while (psize > 0 || (qsize > 0 && q)) {
266 				/* decide whether next element of merge comes from p or q */
267 				if (psize == 0) {
268 					/* p is empty; e must come from q. */
269 					e = q;
270 					q = q->next;
271 					qsize--;
272 				} else if (qsize == 0 || !q) {
273 					/* q is empty; e must come from p. */
274 					e = p;
275 					p = p->next;
276 					psize--;
277 				} else if (sorter(p, q, userData) <= 0) {
278 					/* First element of p is lower (or same);
279 					 * e must come from p. */
280 					e = p;
281 					p = p->next;
282 					psize--;
283 				} else {
284 					/* First element of q is lower; e must come from q. */
285 					e = q;
286 					q = q->next;
287 					qsize--;
288 				}
289 
290 				/* add the next element to the merged list */
291 				if (tail) {
292 					tail->next = e;
293 				} else {
294 					list = e;
295 				}
296 				tail = e;
297 			}
298 
299 			/* now p has stepped `insize' places along, and q has too */
300 			p = q;
301 		}
302 		tail->next = nullptr;
303 
304 		/* If we have done only one merge, we're finished. */
305 		if (nmerges <= 1) /* allow for nmerges==0, the empty list case */
306 			return list;
307 
308 		/* Otherwise repeat, merging lists twice the size */
309 		insize *= 2;
310 	}
311 }
312 
LIST_Sort(linkedList_t ** list,linkedListSort_t sorter,const void * userData)313 void LIST_Sort (linkedList_t** list, linkedListSort_t sorter, const void* userData)
314 {
315 	if (LIST_IsEmpty(*list))
316 		return;
317 	*list = _LIST_Sort(*list, sorter, userData);
318 }
319 
320 /**
321  * @brief Copies the list structure data - but not the content from the original list.
322  * We are only using pointers here.
323  */
LIST_CopyStructure(linkedList_t * src)324 linkedList_t* LIST_CopyStructure (linkedList_t* src)
325 {
326 	linkedList_t* dest = nullptr;
327 	LIST_Foreach(src, void, data) {
328 		LIST_AddPointer(&dest, data);
329 	}
330 	return dest;
331 }
332 
333 /**
334  * @brief Checks whether the given list is empty
335  * @param[in] list The linked list to check
336  * @return @c true if empty, @c false otherwise
337  */
LIST_IsEmpty(const linkedList_t * list)338 bool LIST_IsEmpty (const linkedList_t* list)
339 {
340 	return list == nullptr;
341 }
342 
343 /**
344  * @sa LIST_Add
345  * @sa LIST_RemoveEntry
346  */
LIST_Count(const linkedList_t * list)347 int LIST_Count (const linkedList_t* list)
348 {
349 	const linkedList_t* l = list;
350 	int count = 0;
351 
352 	while (l) {
353 		count++;
354 		l = l->next;
355 	}
356 	return count;
357 }
358 
359 /**
360  * @brief Get an entry of a linked list by its index in the list.
361  * @param[in] list Linked list to get the entry from.
362  * @param[in] index The index of the entry in the linked list.
363  * @return A void pointer of the content in the list-entry.
364  */
LIST_GetByIdx(linkedList_t * list,int index)365 void* LIST_GetByIdx (linkedList_t* list, int index)
366 {
367 	int i;
368 
369 	if (LIST_IsEmpty(list))
370 		return nullptr;
371 
372 	if (index < 0)
373 		return nullptr;
374 
375 	i = 0;
376 	while (list) {
377 		if (i == index)
378 			return list->data;
379 		i++;
380 		list = list->next;
381 	}
382 
383 	return nullptr;
384 }
385 
LIST_GetRandom(linkedList_t * list)386 void* LIST_GetRandom (linkedList_t* list)
387 {
388 	const int elements = LIST_Count(list);
389 	if (elements == 0)
390 		return nullptr;
391 
392 	const int element = rand() % elements;
393 	return LIST_GetByIdx(list, element);
394 }
395