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