Coverage Report

Created: 2026-10-22 06:50

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