Line | Count | Source |
1 | | /*************************************************************************** |
2 | | * _ _ ____ _ |
3 | | * Project ___| | | | _ \| | |
4 | | * / __| | | | |_) | | |
5 | | * | (__| |_| | _ <| |___ |
6 | | * \___|\___/|_| \_\_____| |
7 | | * |
8 | | * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. |
9 | | * |
10 | | * This software is licensed as described in the file COPYING, which |
11 | | * you should have received as part of this distribution. The terms |
12 | | * are also available at https://curl.se/docs/copyright.html. |
13 | | * |
14 | | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
15 | | * copies of the Software, and permit persons to whom the Software is |
16 | | * furnished to do so, under the terms of the COPYING file. |
17 | | * |
18 | | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
19 | | * KIND, either express or implied. |
20 | | * |
21 | | * SPDX-License-Identifier: curl |
22 | | * |
23 | | ***************************************************************************/ |
24 | | #include "curl_setup.h" |
25 | | |
26 | | #include "llist.h" |
27 | | |
28 | | #ifdef DEBUGBUILD |
29 | 265k | #define LLISTINIT 0x100cc001 /* random pattern */ |
30 | 1.34k | #define NODEINIT 0x12344321 /* random pattern */ |
31 | 1.34k | #define NODEREM 0x54321012 /* random pattern */ |
32 | | |
33 | 354k | #define VERIFYNODE(x) verifynode(x) |
34 | | static struct Curl_llist_node *verifynode(struct Curl_llist_node *n) |
35 | 354k | { |
36 | 354k | DEBUGASSERT(!n || (n->_init == NODEINIT)); |
37 | 354k | return n; |
38 | 354k | } |
39 | | #else |
40 | | #define VERIFYNODE(x) x |
41 | | #endif |
42 | | /* |
43 | | * @unittest: 1300 |
44 | | */ |
45 | | void Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor) |
46 | 265k | { |
47 | 265k | l->_size = 0; |
48 | 265k | l->_dtor = dtor; |
49 | 265k | l->_head = NULL; |
50 | 265k | l->_tail = NULL; |
51 | 265k | #ifdef DEBUGBUILD |
52 | 265k | l->_init = LLISTINIT; |
53 | 265k | #endif |
54 | 265k | } |
55 | | |
56 | | /* |
57 | | * Curl_llist_insert_next() |
58 | | * |
59 | | * Inserts a new list element after the given one 'e'. If the given existing |
60 | | * entry is NULL and the list already has elements, the new one will be |
61 | | * inserted first in the list. |
62 | | * |
63 | | * The 'ne' argument should be a pointer into the object to store. |
64 | | * |
65 | | * @unittest: 1300 |
66 | | */ |
67 | | void Curl_llist_insert_next(struct Curl_llist *list, |
68 | | struct Curl_llist_node *e, /* may be NULL */ |
69 | | const void *p, |
70 | | struct Curl_llist_node *ne) |
71 | 1.34k | { |
72 | 1.34k | DEBUGASSERT(list); |
73 | 1.34k | DEBUGASSERT(list->_init == LLISTINIT); |
74 | 1.34k | DEBUGASSERT(ne); |
75 | | |
76 | 1.34k | #ifdef DEBUGBUILD |
77 | 1.34k | ne->_init = NODEINIT; |
78 | 1.34k | #endif |
79 | 1.34k | ne->_ptr = CURL_UNCONST(p); |
80 | 1.34k | ne->_list = list; |
81 | 1.34k | if(list->_size == 0) { |
82 | 1.34k | list->_head = ne; |
83 | 1.34k | list->_head->_prev = NULL; |
84 | 1.34k | list->_head->_next = NULL; |
85 | 1.34k | list->_tail = ne; |
86 | 1.34k | } |
87 | 0 | else { |
88 | | /* if 'e' is NULL here, we insert the new element first in the list */ |
89 | 0 | ne->_next = e ? e->_next : list->_head; |
90 | 0 | ne->_prev = e; |
91 | 0 | if(!e) { |
92 | 0 | list->_head->_prev = ne; |
93 | 0 | list->_head = ne; |
94 | 0 | } |
95 | 0 | else if(e->_next) { |
96 | 0 | e->_next->_prev = ne; |
97 | 0 | } |
98 | 0 | else { |
99 | 0 | list->_tail = ne; |
100 | 0 | } |
101 | 0 | if(e) |
102 | 0 | e->_next = ne; |
103 | 0 | } |
104 | | |
105 | 1.34k | ++list->_size; |
106 | 1.34k | } |
107 | | |
108 | | /* |
109 | | * Curl_llist_append() |
110 | | * |
111 | | * Adds a new list element to the end of the list. |
112 | | * |
113 | | * The 'ne' argument should be a pointer into the object to store. |
114 | | * |
115 | | * @unittest: 1300 |
116 | | */ |
117 | | void Curl_llist_append(struct Curl_llist *list, const void *p, |
118 | | struct Curl_llist_node *ne) |
119 | 1.34k | { |
120 | 1.34k | DEBUGASSERT(list); |
121 | 1.34k | DEBUGASSERT(list->_init == LLISTINIT); |
122 | 1.34k | DEBUGASSERT(ne); |
123 | 1.34k | Curl_llist_insert_next(list, list->_tail, p, ne); |
124 | 1.34k | } |
125 | | |
126 | | void *Curl_node_take_elem(struct Curl_llist_node *e) |
127 | 1.34k | { |
128 | 1.34k | void *ptr; |
129 | 1.34k | struct Curl_llist *list; |
130 | 1.34k | if(!e) |
131 | 0 | return NULL; |
132 | | |
133 | 1.34k | list = e->_list; |
134 | 1.34k | DEBUGASSERT(list); |
135 | 1.34k | DEBUGASSERT(list->_init == LLISTINIT); |
136 | 1.34k | DEBUGASSERT(list->_size); |
137 | 1.34k | DEBUGASSERT(e->_init == NODEINIT); |
138 | 1.34k | if(list) { |
139 | 1.34k | if(e == list->_head) { |
140 | 1.34k | list->_head = e->_next; |
141 | | |
142 | 1.34k | if(!list->_head) |
143 | 1.34k | list->_tail = NULL; |
144 | 0 | else |
145 | 0 | e->_next->_prev = NULL; |
146 | 1.34k | } |
147 | 0 | else { |
148 | 0 | if(e->_prev) |
149 | 0 | e->_prev->_next = e->_next; |
150 | |
|
151 | 0 | if(!e->_next) |
152 | 0 | list->_tail = e->_prev; |
153 | 0 | else |
154 | 0 | e->_next->_prev = e->_prev; |
155 | 0 | } |
156 | 1.34k | --list->_size; |
157 | 1.34k | } |
158 | 1.34k | ptr = e->_ptr; |
159 | | |
160 | 1.34k | e->_list = NULL; |
161 | 1.34k | e->_ptr = NULL; |
162 | 1.34k | e->_prev = NULL; |
163 | 1.34k | e->_next = NULL; |
164 | 1.34k | #ifdef DEBUGBUILD |
165 | 1.34k | e->_init = NODEREM; /* specific pattern on remove - not zero */ |
166 | 1.34k | #endif |
167 | | |
168 | 1.34k | return ptr; |
169 | 1.34k | } |
170 | | |
171 | | /* |
172 | | * @unittest: 1300 |
173 | | */ |
174 | | UNITTEST void Curl_node_uremove(struct Curl_llist_node *e, void *user); |
175 | | UNITTEST void Curl_node_uremove(struct Curl_llist_node *e, void *user) |
176 | 1.34k | { |
177 | 1.34k | struct Curl_llist *list; |
178 | 1.34k | void *ptr; |
179 | 1.34k | if(!e) |
180 | 0 | return; |
181 | | |
182 | 1.34k | list = e->_list; |
183 | 1.34k | DEBUGASSERT(list); |
184 | 1.34k | if(list) { |
185 | 1.34k | ptr = Curl_node_take_elem(e); |
186 | 1.34k | if(list->_dtor) |
187 | 0 | list->_dtor(user, ptr); |
188 | 1.34k | } |
189 | 1.34k | } |
190 | | |
191 | | void Curl_node_remove(struct Curl_llist_node *e) |
192 | 1.34k | { |
193 | 1.34k | Curl_node_uremove(e, NULL); |
194 | 1.34k | } |
195 | | |
196 | | void Curl_llist_destroy(struct Curl_llist *list, void *user) |
197 | 0 | { |
198 | 0 | if(list) { |
199 | 0 | DEBUGASSERT(list->_init == LLISTINIT); |
200 | 0 | while(list->_size > 0) |
201 | 0 | Curl_node_uremove(list->_tail, user); |
202 | 0 | } |
203 | 0 | } |
204 | | |
205 | | /* Curl_llist_head() returns the first 'struct Curl_llist_node *', which |
206 | | might be NULL */ |
207 | | struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list) |
208 | 353k | { |
209 | 353k | DEBUGASSERT(list); |
210 | 353k | DEBUGASSERT(list->_init == LLISTINIT); |
211 | 353k | return VERIFYNODE(list->_head); |
212 | 353k | } |
213 | | |
214 | | #ifdef UNITTESTS |
215 | | /* Curl_llist_tail() returns the last 'struct Curl_llist_node *', which |
216 | | might be NULL */ |
217 | | struct Curl_llist_node *Curl_llist_tail(struct Curl_llist *list) |
218 | | { |
219 | | DEBUGASSERT(list); |
220 | | DEBUGASSERT(list->_init == LLISTINIT); |
221 | | return VERIFYNODE(list->_tail); |
222 | | } |
223 | | #endif |
224 | | |
225 | | /* Curl_llist_count() returns a size_t the number of nodes in the list */ |
226 | | size_t Curl_llist_count(struct Curl_llist *list) |
227 | 0 | { |
228 | 0 | DEBUGASSERT(list); |
229 | 0 | DEBUGASSERT(list->_init == LLISTINIT); |
230 | 0 | return list->_size; |
231 | 0 | } |
232 | | |
233 | | /* Curl_node_elem() returns the custom data from a Curl_llist_node */ |
234 | | void *Curl_node_elem(struct Curl_llist_node *n) |
235 | 1.34k | { |
236 | 1.34k | DEBUGASSERT(n); |
237 | 1.34k | DEBUGASSERT(n->_init == NODEINIT); |
238 | 1.34k | return n->_ptr; |
239 | 1.34k | } |
240 | | |
241 | | /* Curl_node_next() returns the next element in a list from a given |
242 | | Curl_llist_node */ |
243 | | struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n) |
244 | 1.34k | { |
245 | 1.34k | DEBUGASSERT(n); |
246 | 1.34k | DEBUGASSERT(n->_init == NODEINIT); |
247 | 1.34k | return VERIFYNODE(n->_next); |
248 | 1.34k | } |
249 | | |
250 | | #ifdef UNITTESTS |
251 | | |
252 | | /* Curl_node_prev() returns the previous element in a list from a given |
253 | | Curl_llist_node */ |
254 | | struct Curl_llist_node *Curl_node_prev(struct Curl_llist_node *n) |
255 | | { |
256 | | DEBUGASSERT(n); |
257 | | DEBUGASSERT(n->_init == NODEINIT); |
258 | | return VERIFYNODE(n->_prev); |
259 | | } |
260 | | |
261 | | #endif |
262 | | |
263 | | struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n) |
264 | 0 | { |
265 | 0 | DEBUGASSERT(n); |
266 | 0 | DEBUGASSERT(!n->_list || n->_init == NODEINIT); |
267 | 0 | return n->_list; |
268 | 0 | } |