llist.c (6546B)
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 25 #include "curl_setup.h" 26 27 #include <curl/curl.h> 28 29 #include "llist.h" 30 #include "curl_memory.h" 31 32 /* this must be the last include file */ 33 #include "memdebug.h" 34 35 #ifdef DEBUGBUILD 36 #define LLISTINIT 0x100cc001 /* random pattern */ 37 #define NODEINIT 0x12344321 /* random pattern */ 38 #define NODEREM 0x54321012 /* random pattern */ 39 40 #define VERIFYNODE(x) verifynode(x) 41 static struct Curl_llist_node *verifynode(struct Curl_llist_node *n) 42 { 43 DEBUGASSERT(!n || (n->_init == NODEINIT)); 44 return n; 45 } 46 #else 47 #define VERIFYNODE(x) x 48 #endif 49 /* 50 * @unittest: 1300 51 */ 52 void 53 Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor) 54 { 55 l->_size = 0; 56 l->_dtor = dtor; 57 l->_head = NULL; 58 l->_tail = NULL; 59 #ifdef DEBUGBUILD 60 l->_init = LLISTINIT; 61 #endif 62 } 63 64 /* 65 * Curl_llist_insert_next() 66 * 67 * Inserts a new list element after the given one 'e'. If the given existing 68 * entry is NULL and the list already has elements, the new one will be 69 * inserted first in the list. 70 * 71 * The 'ne' argument should be a pointer into the object to store. 72 * 73 * @unittest: 1300 74 */ 75 void 76 Curl_llist_insert_next(struct Curl_llist *list, 77 struct Curl_llist_node *e, /* may be NULL */ 78 const void *p, 79 struct Curl_llist_node *ne) 80 { 81 DEBUGASSERT(list); 82 DEBUGASSERT(list->_init == LLISTINIT); 83 DEBUGASSERT(ne); 84 85 #ifdef DEBUGBUILD 86 ne->_init = NODEINIT; 87 #endif 88 ne->_ptr = CURL_UNCONST(p); 89 ne->_list = list; 90 if(list->_size == 0) { 91 list->_head = ne; 92 list->_head->_prev = NULL; 93 list->_head->_next = NULL; 94 list->_tail = ne; 95 } 96 else { 97 /* if 'e' is NULL here, we insert the new element first in the list */ 98 ne->_next = e ? e->_next : list->_head; 99 ne->_prev = e; 100 if(!e) { 101 list->_head->_prev = ne; 102 list->_head = ne; 103 } 104 else if(e->_next) { 105 e->_next->_prev = ne; 106 } 107 else { 108 list->_tail = ne; 109 } 110 if(e) 111 e->_next = ne; 112 } 113 114 ++list->_size; 115 } 116 117 /* 118 * Curl_llist_append() 119 * 120 * Adds a new list element to the end of the list. 121 * 122 * The 'ne' argument should be a pointer into the object to store. 123 * 124 * @unittest: 1300 125 */ 126 void 127 Curl_llist_append(struct Curl_llist *list, const void *p, 128 struct Curl_llist_node *ne) 129 { 130 DEBUGASSERT(list); 131 DEBUGASSERT(list->_init == LLISTINIT); 132 DEBUGASSERT(ne); 133 Curl_llist_insert_next(list, list->_tail, p, ne); 134 } 135 136 void *Curl_node_take_elem(struct Curl_llist_node *e) 137 { 138 void *ptr; 139 struct Curl_llist *list; 140 if(!e) 141 return NULL; 142 143 list = e->_list; 144 DEBUGASSERT(list); 145 DEBUGASSERT(list->_init == LLISTINIT); 146 DEBUGASSERT(list->_size); 147 DEBUGASSERT(e->_init == NODEINIT); 148 if(list) { 149 if(e == list->_head) { 150 list->_head = e->_next; 151 152 if(!list->_head) 153 list->_tail = NULL; 154 else 155 e->_next->_prev = NULL; 156 } 157 else { 158 if(e->_prev) 159 e->_prev->_next = e->_next; 160 161 if(!e->_next) 162 list->_tail = e->_prev; 163 else 164 e->_next->_prev = e->_prev; 165 } 166 --list->_size; 167 } 168 ptr = e->_ptr; 169 170 e->_list = NULL; 171 e->_ptr = NULL; 172 e->_prev = NULL; 173 e->_next = NULL; 174 #ifdef DEBUGBUILD 175 e->_init = NODEREM; /* specific pattern on remove - not zero */ 176 #endif 177 178 return ptr; 179 } 180 181 /* 182 * @unittest: 1300 183 */ 184 UNITTEST void Curl_node_uremove(struct Curl_llist_node *, void *); 185 UNITTEST void 186 Curl_node_uremove(struct Curl_llist_node *e, void *user) 187 { 188 struct Curl_llist *list; 189 void *ptr; 190 if(!e) 191 return; 192 193 list = e->_list; 194 DEBUGASSERT(list); 195 if(list) { 196 ptr = Curl_node_take_elem(e); 197 if(list->_dtor) 198 list->_dtor(user, ptr); 199 } 200 } 201 202 void Curl_node_remove(struct Curl_llist_node *e) 203 { 204 Curl_node_uremove(e, NULL); 205 } 206 207 void 208 Curl_llist_destroy(struct Curl_llist *list, void *user) 209 { 210 if(list) { 211 DEBUGASSERT(list->_init == LLISTINIT); 212 while(list->_size > 0) 213 Curl_node_uremove(list->_tail, user); 214 } 215 } 216 217 /* Curl_llist_head() returns the first 'struct Curl_llist_node *', which 218 might be NULL */ 219 struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list) 220 { 221 DEBUGASSERT(list); 222 DEBUGASSERT(list->_init == LLISTINIT); 223 return VERIFYNODE(list->_head); 224 } 225 226 #ifdef UNITTESTS 227 /* Curl_llist_tail() returns the last 'struct Curl_llist_node *', which 228 might be NULL */ 229 struct Curl_llist_node *Curl_llist_tail(struct Curl_llist *list) 230 { 231 DEBUGASSERT(list); 232 DEBUGASSERT(list->_init == LLISTINIT); 233 return VERIFYNODE(list->_tail); 234 } 235 #endif 236 237 /* Curl_llist_count() returns a size_t the number of nodes in the list */ 238 size_t Curl_llist_count(struct Curl_llist *list) 239 { 240 DEBUGASSERT(list); 241 DEBUGASSERT(list->_init == LLISTINIT); 242 return list->_size; 243 } 244 245 /* Curl_node_elem() returns the custom data from a Curl_llist_node */ 246 void *Curl_node_elem(struct Curl_llist_node *n) 247 { 248 DEBUGASSERT(n); 249 DEBUGASSERT(n->_init == NODEINIT); 250 return n->_ptr; 251 } 252 253 /* Curl_node_next() returns the next element in a list from a given 254 Curl_llist_node */ 255 struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n) 256 { 257 DEBUGASSERT(n); 258 DEBUGASSERT(n->_init == NODEINIT); 259 return VERIFYNODE(n->_next); 260 } 261 262 #ifdef UNITTESTS 263 264 /* Curl_node_prev() returns the previous element in a list from a given 265 Curl_llist_node */ 266 struct Curl_llist_node *Curl_node_prev(struct Curl_llist_node *n) 267 { 268 DEBUGASSERT(n); 269 DEBUGASSERT(n->_init == NODEINIT); 270 return VERIFYNODE(n->_prev); 271 } 272 273 #endif 274 275 struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n) 276 { 277 DEBUGASSERT(n); 278 DEBUGASSERT(!n->_list || n->_init == NODEINIT); 279 return n->_list; 280 }