1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 2015-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 * SPDX-License-Identifier: MIT
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23 #include "containers/list.h"
24 #include "utils/nvassert.h"
25
26 CONT_VTABLE_DEFN(ListBase, listIterRange_IMPL, NULL);
27
28 #if PORT_IS_CHECKED_BUILD
29 static NvBool _listIterRangeCheck(ListBase *pList, ListNode *pFirst,
30 ListNode *pLast);
31 #endif
32 static void _listInsertBase(ListBase *pList, void *pNext, void *pValue);
33
listInit_IMPL(NonIntrusiveList * pList,PORT_MEM_ALLOCATOR * pAllocator,NvU32 valueSize)34 void listInit_IMPL(NonIntrusiveList *pList, PORT_MEM_ALLOCATOR *pAllocator,
35 NvU32 valueSize)
36 {
37 NV_ASSERT_OR_RETURN_VOID(NULL != pList);
38 NV_ASSERT_OR_RETURN_VOID(NULL != pAllocator);
39
40 portMemSet(&(pList->base), 0, sizeof(pList->base));
41 CONT_VTABLE_INIT(ListBase, &pList->base);
42 pList->pAllocator = pAllocator;
43 pList->valueSize = valueSize;
44 pList->base.nodeOffset = (NvS32)(0 - sizeof(ListNode));
45 }
46
listInitIntrusive_IMPL(IntrusiveList * pList,NvS32 nodeOffset)47 void listInitIntrusive_IMPL(IntrusiveList *pList, NvS32 nodeOffset)
48 {
49 NV_ASSERT_OR_RETURN_VOID(NULL != pList);
50 portMemSet(&(pList->base), 0, sizeof(pList->base));
51 CONT_VTABLE_INIT(ListBase, &pList->base);
52 pList->base.nodeOffset = nodeOffset;
53 }
54
55 static void
_listDestroy(ListBase * pList,PORT_MEM_ALLOCATOR * pAllocator)56 _listDestroy(ListBase *pList, PORT_MEM_ALLOCATOR *pAllocator)
57 {
58 ListNode *pNode;
59 NV_ASSERT_OR_RETURN_VOID(NULL != pList);
60
61 pNode = pList->pHead;
62
63 pList->pHead = NULL;
64 pList->pTail = NULL;
65 pList->count = 0;
66 NV_CHECKED_ONLY(pList->versionNumber++);
67
68 while (pNode != NULL)
69 {
70 ListNode *pTemp = pNode;
71 pNode = pNode->pNext;
72 pTemp->pPrev = NULL;
73 pTemp->pNext = NULL;
74 NV_CHECKED_ONLY(pTemp->pList = NULL);
75 if (NULL != pAllocator)
76 {
77 PORT_FREE(pAllocator, pTemp);
78 }
79 }
80 }
81
listDestroy_IMPL(NonIntrusiveList * pList)82 void listDestroy_IMPL(NonIntrusiveList *pList)
83 {
84 _listDestroy(&pList->base, pList->pAllocator);
85 }
86
listDestroyIntrusive_IMPL(ListBase * pList)87 void listDestroyIntrusive_IMPL(ListBase *pList)
88 {
89 _listDestroy(pList, NULL);
90 }
91
listCount_IMPL(ListBase * pList)92 NvU32 listCount_IMPL(ListBase *pList)
93 {
94 NV_ASSERT_OR_RETURN(pList, 0);
95 return pList->count;
96 }
97
listInsertNew_IMPL(NonIntrusiveList * pList,void * pNext)98 void *listInsertNew_IMPL(NonIntrusiveList *pList, void *pNext)
99 {
100 void *pNode = NULL;
101 void *pValue;
102
103 NV_ASSERT_OR_RETURN(NULL != pList, NULL);
104
105 pNode = PORT_ALLOC(pList->pAllocator, sizeof(ListNode) + pList->valueSize);
106 NV_ASSERT_OR_RETURN(NULL != pNode, NULL);
107
108 portMemSet(pNode, 0, sizeof(ListNode) + pList->valueSize);
109 pValue = listNodeToValue(&pList->base, pNode);
110 _listInsertBase(&(pList->base), pNext, pValue);
111
112 return pValue;
113 }
114
listAppendNew_IMPL(NonIntrusiveList * pList)115 void *listAppendNew_IMPL(NonIntrusiveList *pList)
116 {
117 return listInsertNew_IMPL(pList, NULL);
118 }
119
listPrependNew_IMPL(NonIntrusiveList * pList)120 void *listPrependNew_IMPL(NonIntrusiveList *pList)
121 {
122 return listInsertNew_IMPL(pList, listHead_IMPL(&(pList->base)));
123 }
124
listInsertValue_IMPL(NonIntrusiveList * pList,void * pNext,const void * pValue)125 void *listInsertValue_IMPL
126 (
127 NonIntrusiveList *pList,
128 void *pNext,
129 const void *pValue
130 )
131 {
132 void *pCurrent;
133
134 NV_ASSERT_OR_RETURN(NULL != pValue, NULL);
135
136 pCurrent = listInsertNew_IMPL(pList, pNext);
137 if (NULL == pCurrent)
138 return NULL;
139
140 return portMemCopy(pCurrent, pList->valueSize, pValue, pList->valueSize);
141 }
142
listAppendValue_IMPL(NonIntrusiveList * pList,const void * pValue)143 void *listAppendValue_IMPL(NonIntrusiveList *pList, const void *pValue)
144 {
145 return listInsertValue_IMPL(pList, NULL, pValue);
146 }
147
listPrependValue_IMPL(NonIntrusiveList * pList,const void * pValue)148 void *listPrependValue_IMPL(NonIntrusiveList *pList, const void *pValue)
149 {
150 return listInsertValue_IMPL(pList, listHead_IMPL(&(pList->base)), pValue);
151 }
152
listInsertExisting_IMPL(IntrusiveList * pList,void * pNext,void * pValue)153 void listInsertExisting_IMPL(IntrusiveList *pList, void *pNext, void *pValue)
154 {
155 NV_ASSERT_OR_RETURN_VOID(NULL != pList);
156 NV_ASSERT_OR_RETURN_VOID(NULL != pValue);
157 _listInsertBase(&(pList->base), pNext, pValue);
158 }
159
listAppendExisting_IMPL(IntrusiveList * pList,void * pValue)160 void listAppendExisting_IMPL(IntrusiveList *pList, void *pValue)
161 {
162 listInsertExisting_IMPL(pList, NULL, pValue);
163 }
164
listPrependExisting_IMPL(IntrusiveList * pList,void * pValue)165 void listPrependExisting_IMPL(IntrusiveList *pList, void *pValue)
166 {
167 listInsertExisting_IMPL(pList, listHead_IMPL(&(pList->base)), pValue);
168 }
169
170 // for nonintrusive version
listRemove_IMPL(NonIntrusiveList * pList,void * pValue)171 void listRemove_IMPL(NonIntrusiveList *pList, void *pValue)
172 {
173 if (pValue == NULL)
174 return;
175 listRemoveIntrusive_IMPL(&(pList->base), pValue);
176 PORT_FREE(pList->pAllocator, listValueToNode(&pList->base, pValue));
177 }
178
179 // intrusive version
listRemoveIntrusive_IMPL(ListBase * pList,void * pValue)180 void listRemoveIntrusive_IMPL
181 (
182 ListBase *pList,
183 void *pValue
184 )
185 {
186 ListNode *pNode;
187
188 if (pValue == NULL)
189 return;
190
191 pNode = listValueToNode(pList, pValue);
192 NV_ASSERT_OR_RETURN_VOID(NULL != pNode);
193 NV_ASSERT_CHECKED(pNode->pList == pList);
194
195 if (pNode->pPrev != NULL)
196 pNode->pPrev->pNext = pNode->pNext;
197 else
198 pList->pHead = pNode->pNext;
199
200 if (pNode->pNext != NULL)
201 pNode->pNext->pPrev = pNode->pPrev;
202 else
203 pList->pTail = pNode->pPrev;
204
205 pNode->pNext = NULL;
206 pNode->pPrev = NULL;
207
208 pList->count--;
209 NV_CHECKED_ONLY(pList->versionNumber++);
210 NV_CHECKED_ONLY(pNode->pList = NULL);
211 }
212
213 // pvalue here means the value
listRemoveFirstByValue_IMPL(NonIntrusiveList * pList,void * pValue)214 void listRemoveFirstByValue_IMPL
215 (
216 NonIntrusiveList *pList,
217 void *pValue
218 )
219 {
220 void *pValueFound = listFindByValue_IMPL(pList, pValue);
221 if (pValueFound)
222 {
223 listRemove_IMPL(pList, pValueFound);
224 }
225 }
226
listRemoveAllByValue_IMPL(NonIntrusiveList * pList,void * pValue)227 void listRemoveAllByValue_IMPL
228 (
229 NonIntrusiveList *pList,
230 void *pValue
231 )
232 {
233 void *pValueFound;
234 ListNode *pNode;
235
236 NV_ASSERT_OR_RETURN_VOID(NULL != pList);
237 NV_ASSERT_OR_RETURN_VOID(NULL != pValue);
238
239 pNode = pList->base.pHead;
240 while (pNode != NULL)
241 {
242 pValueFound = listNodeToValue(&pList->base, pNode);
243 pNode = pNode->pNext;
244
245 if (portMemCmp(pValueFound, pValue, pList->valueSize) == 0)
246 {
247 listRemove_IMPL(pList, pValueFound);
248 pValueFound = NULL;
249 }
250 }
251 }
252
listFindByValue_IMPL(NonIntrusiveList * pList,void * pValue)253 void *listFindByValue_IMPL
254 (
255 NonIntrusiveList *pList,
256 void *pValue
257 )
258 {
259 void *pResult;
260 ListNode *pNode;
261
262 NV_ASSERT_OR_RETURN(NULL != pList, NULL);
263 NV_ASSERT_OR_RETURN(NULL != pValue, NULL);
264
265 pNode = pList->base.pHead;
266 while (pNode != NULL)
267 {
268 pResult = listNodeToValue(&pList->base, pNode);
269
270 if (portMemCmp(pResult, pValue, pList->valueSize) == 0)
271 return pResult;
272
273 pNode = pNode->pNext;
274 }
275
276 return NULL;
277 }
278
listHead_IMPL(ListBase * pList)279 void *listHead_IMPL
280 (
281 ListBase *pList
282 )
283 {
284 NV_ASSERT_OR_RETURN(NULL != pList, NULL);
285 return listNodeToValue(pList, pList->pHead);
286 }
287
listTail_IMPL(ListBase * pList)288 void *listTail_IMPL
289 (
290 ListBase *pList
291 )
292 {
293 NV_ASSERT_OR_RETURN(NULL != pList, NULL);
294 return listNodeToValue(pList, pList->pTail);
295 }
296
listNext_IMPL(ListBase * pList,void * pValue)297 void *listNext_IMPL
298 (
299 ListBase *pList,
300 void *pValue
301 )
302 {
303 ListNode *pNode = listValueToNode(pList, pValue);
304 NV_ASSERT_OR_RETURN(NULL != pNode, NULL);
305 NV_ASSERT_CHECKED(pNode->pList == pList);
306 return listNodeToValue(pList, pNode->pNext);
307 }
308
listPrev_IMPL(ListBase * pList,void * pValue)309 void *listPrev_IMPL
310 (
311 ListBase *pList,
312 void *pValue
313 )
314 {
315 ListNode *pNode = listValueToNode(pList, pValue);
316 NV_ASSERT_OR_RETURN(NULL != pNode, NULL);
317 NV_ASSERT_CHECKED(pNode->pList == pList);
318 return listNodeToValue(pList, pNode->pPrev);
319 }
320
listIterRange_IMPL(ListBase * pList,void * pFirst,void * pLast)321 ListIterBase listIterRange_IMPL
322 (
323 ListBase *pList,
324 void *pFirst,
325 void *pLast
326 )
327 {
328 ListIterBase it;
329
330 NV_ASSERT(NULL != pList);
331
332 NV_CHECKED_ONLY(it.versionNumber = pList->versionNumber);
333 it.pList = pList;
334 it.pNode = listValueToNode(pList, pFirst);
335 it.pLast = listValueToNode(pList, pLast);
336 it.pValue = NULL;
337
338 NV_ASSERT_CHECKED(it.pNode == NULL || it.pNode->pList == pList);
339 NV_ASSERT_CHECKED(it.pLast == NULL || it.pLast->pList == pList);
340 NV_ASSERT_CHECKED(_listIterRangeCheck(pList, it.pNode, it.pLast));
341 NV_CHECKED_ONLY(it.bValid = NV_TRUE);
342
343 return it;
344 }
345
listIterNext_IMPL(ListIterBase * pIt)346 NvBool listIterNext_IMPL(ListIterBase *pIt)
347 {
348 NV_ASSERT_OR_RETURN(NULL != pIt, NV_FALSE);
349
350 #if PORT_IS_CHECKED_BUILD
351 if (pIt->bValid && !CONT_ITER_IS_VALID(pIt->pList, pIt))
352 {
353 NV_ASSERT(CONT_ITER_IS_VALID(pIt->pList, pIt));
354 PORT_DUMP_STACK();
355
356 pIt->bValid = NV_FALSE;
357 }
358 #endif
359
360 if (!pIt->pNode)
361 return NV_FALSE;
362
363 pIt->pValue = listNodeToValue(pIt->pList, pIt->pNode);
364
365 if (pIt->pNode == pIt->pLast)
366 pIt->pNode = NULL;
367 else
368 pIt->pNode = pIt->pNode->pNext;
369
370 return NV_TRUE;
371 }
372
373 #if PORT_IS_CHECKED_BUILD
374 // @todo: optimize for best average complexity
375 // assumption: nodes ownership checked in the caller function
376 // allow same node
_listIterRangeCheck(ListBase * pList,ListNode * pFirst,ListNode * pLast)377 static NvBool _listIterRangeCheck
378 (
379 ListBase *pList,
380 ListNode *pFirst,
381 ListNode *pLast
382 )
383 {
384 ListNode *pNode;
385
386 for (pNode = pFirst; pNode != NULL; pNode = pNode->pNext)
387 {
388 if (pNode == pLast)
389 return NV_TRUE;
390 }
391
392 // Check for both NULL (empty range) case.
393 return pNode == pLast;
394 }
395 #endif
396
_listInsertBase(ListBase * pList,void * pNextValue,void * pValue)397 static void _listInsertBase
398 (
399 ListBase *pList,
400 void *pNextValue,
401 void *pValue
402 )
403 {
404 ListNode *pNext = listValueToNode(pList, pNextValue);
405 ListNode *pNode = listValueToNode(pList, pValue);
406
407 pNode->pPrev = pNext ? pNext->pPrev : pList->pTail;
408 pNode->pNext = pNext;
409
410 if (pNode->pPrev)
411 pNode->pPrev->pNext = pNode;
412 else
413 pList->pHead = pNode;
414
415 if (pNode->pNext)
416 pNode->pNext->pPrev = pNode;
417 else
418 pList->pTail = pNode;
419
420 pList->count++;
421 NV_CHECKED_ONLY(pList->versionNumber++);
422 NV_CHECKED_ONLY(pNode->pList = pList);
423 }
424
listIsValid_IMPL(void * pList)425 NvBool listIsValid_IMPL(void *pList)
426 {
427 #if NV_TYPEOF_SUPPORTED
428 return NV_TRUE;
429 #else
430 if (CONT_VTABLE_VALID((ListBase*)pList))
431 return NV_TRUE;
432
433 NV_ASSERT_FAILED("vtable not valid!");
434 CONT_VTABLE_INIT(ListBase, (ListBase*)pList);
435 return NV_FALSE;
436 #endif
437 }
438
439