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