Coverage Report

Created: 2026-01-31 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wget2/libwget/vector.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2012 Tim Ruehsen
3
 * Copyright (c) 2015-2024 Free Software Foundation, Inc.
4
 *
5
 * This file is part of libwget.
6
 *
7
 * Libwget is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Lesser General Public License as published by
9
 * the Free Software Foundation, either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * Libwget is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with libwget.  If not, see <https://www.gnu.org/licenses/>.
19
 *
20
 *
21
 * vector routines
22
 *
23
 * Changelog
24
 * 25.04.2012  Tim Ruehsen  created
25
 *
26
 */
27
28
#include <config.h>
29
30
#include <stdlib.h>
31
#include <string.h>
32
#include <stdarg.h>
33
34
#include <wget.h>
35
#include "private.h"
36
37
struct wget_vector_st {
38
  wget_vector_compare_fn
39
    *cmp; //!< comparison function for sorting and searching
40
  wget_vector_destructor
41
    *destructor; //!< element destructor function
42
  void
43
    **entry; //!< pointer to array of pointers to elements
44
  int
45
    max,     //!< allocated elements
46
    cur;     //!< number of elements in use
47
  bool
48
    sorted : 1; //!< 1=list is sorted, 0=list is not sorted
49
  float
50
    resize_factor; //!< factor to calculate new vector size
51
};
52
53
/**
54
 * \file
55
 * \brief Vector functions
56
 * \defgroup libwget-vector Vector functions
57
 * @{
58
 *
59
 * Functions to realize vectors (growable arrays).
60
 */
61
62
/**
63
 * \param[in] max Initial number of pre-allocated entries.
64
 * \param[in] cmp Comparison function for sorting/finding/sorted insertion or %NULL.
65
 * \return New vector instance
66
 *
67
 * Create a new vector instance, to be free'd after use with wget_vector_free().
68
 */
69
wget_vector *wget_vector_create(int max, wget_vector_compare_fn *cmp)
70
53.2k
{
71
53.2k
  wget_vector *v = wget_calloc(1, sizeof(wget_vector));
72
73
53.2k
  if (!v)
74
0
    return NULL;
75
76
53.2k
  if (!(v->entry = wget_malloc(max * sizeof(void *)))) {
77
0
    xfree(v);
78
0
    return NULL;
79
0
  }
80
81
53.2k
  v->max = max;
82
53.2k
  v->resize_factor = 2;
83
53.2k
  v->cmp = cmp;
84
53.2k
  v->destructor = free;
85
86
53.2k
  return v;
87
53.2k
}
88
89
/**
90
 * \param[in] v Vector
91
 * \param[in] factor Vector growth factor
92
 *
93
 * Set the factor for resizing the vector when it is full.
94
 *
95
 * The new size is 'factor * oldsize'. If the new size is less or equal the old size,
96
 * the involved insertion function will return an error and the internal state of
97
 * the vector will not change.
98
 *
99
 * Default is 2.
100
 */
101
void wget_vector_set_resize_factor(wget_vector *v, float factor)
102
0
{
103
0
  if (v)
104
0
    v->resize_factor = factor;
105
0
}
106
107
static int WGET_GCC_NONNULL((2)) insert_element(wget_vector *v, const void *elem, int pos, int replace)
108
849k
{
109
849k
  if (pos < 0 || !v || pos > v->cur)
110
0
    return WGET_E_INVALID;
111
112
849k
  if (!replace) {
113
831k
    if (v->max == v->cur) {
114
6.38k
      int newsize = (int) (v->max * v->resize_factor);
115
116
6.38k
      if (newsize <= v->max)
117
0
        return WGET_E_INVALID;
118
119
6.38k
      void **tmp = wget_realloc(v->entry, newsize * sizeof(void *));
120
6.38k
      if (!tmp)
121
0
        return WGET_E_MEMORY;
122
123
6.38k
      v->entry = tmp;
124
6.38k
      v->max = newsize;
125
6.38k
    }
126
127
831k
    memmove(&v->entry[pos + 1], &v->entry[pos], (v->cur - pos) * sizeof(void *));
128
831k
    v->cur++;
129
831k
  }
130
131
849k
  v->entry[pos] = (void *) elem;
132
133
849k
  if (v->cmp) {
134
89.1k
    if (v->cur == 1) v->sorted = 1;
135
79.4k
    else if (v->cur > 1 && v->sorted) {
136
26.6k
      if (pos == 0) {
137
1.27k
        if (v->cmp(elem, v->entry[1]) > 0) v->sorted = 0;
138
25.4k
      } else if (pos == v->cur - 1) {
139
21.9k
        if (v->cmp(elem, v->entry[v->cur - 2]) < 0) v->sorted = 0;
140
21.9k
      } else {
141
3.47k
        if (v->cmp(elem, v->entry[pos - 1]) < 0 ||
142
3.41k
          v->cmp(elem, v->entry[pos + 1]) > 0) {
143
113
          v->sorted = 0;
144
113
        }
145
3.47k
      }
146
26.6k
    }
147
89.1k
  }
148
149
849k
  return pos; // return position of new element
150
849k
}
151
152
/**
153
 * \param[in] v Vector where \p elem is inserted into
154
 * \param[in] elem Element to insert into \p v
155
 * \param[in] pos Position to insert \p elem at
156
 * \return Index of inserted element (>= 0) or WGET_E_*  on error (< 0)
157
 *
158
 * Insert \p elem of at index \p pos.
159
 *
160
 * \p elem is *not* cloned, the vector takes 'ownership' of the element.
161
 *
162
 * An error is returned if \p v is %NULL or \p pos is out of range (< 0 or > # of entries).
163
 */
164
int wget_vector_insert(wget_vector *v, const void *elem, int pos)
165
0
{
166
0
  return insert_element(v, elem, pos, 0);
167
0
}
168
169
/**
170
 * \param[in] v Vector where \p elem is inserted into
171
 * \param[in] elem Element to insert into \p v
172
 * \return Index of inserted element (>= 0) or WGET_E_*  on error (< 0)
173
 *
174
 * Insert \p elem of at a position that keeps the sort order of the elements.
175
 * If the vector has no comparison function, \p elem will be inserted as the last element.
176
 * If the elements in the vector are not sorted, they will be sorted after returning from this function.
177
 *
178
 * \p elem is *not* cloned, the vector takes 'ownership' of the element.
179
 *
180
 * An error is returned if \p v is %NULL.
181
 */
182
int wget_vector_insert_sorted(wget_vector *v, const void *elem)
183
4.90k
{
184
4.90k
  if (!v)
185
0
    return WGET_E_INVALID;
186
187
4.90k
  if (!v->cmp)
188
0
    return insert_element(v, elem, v->cur, 0);
189
190
4.90k
  if (!v->sorted)
191
2.24k
    wget_vector_sort(v);
192
193
  // binary search for element
194
4.90k
  int l = 0, r = v->cur - 1, m = 0, res = 0;
195
196
11.5k
  while (l <= r) {
197
6.66k
    m = (l + r) / 2;
198
6.66k
    if ((res = v->cmp(elem, v->entry[m])) > 0) l = m + 1;
199
3.51k
    else if (res < 0) r = m - 1;
200
0
    else return insert_element(v, elem, m, 0);
201
6.66k
  }
202
4.90k
  if (res > 0) m++;
203
204
4.90k
  return insert_element(v, elem, m, 0);
205
4.90k
}
206
207
/**
208
 * \param[in] v Vector where \p elem is appended to
209
 * \param[in] elem Element to append to a \p v
210
 * \param[in] size Size of \p elem
211
 * \return Index of inserted element (>= 0) or WGET_E_*  on error (< 0)
212
 *
213
 * Append \p elem of given \p size to vector \p v.
214
 *
215
 * \p elem is cloned / copied (shallow).
216
 *
217
 * An error is returned if \p v is %NULL.
218
 */
219
int wget_vector_add_memdup(wget_vector *v, const void *elem, size_t size)
220
220k
{
221
220k
  void *elemp;
222
220k
  int rc;
223
224
220k
  if (!v)
225
0
    return WGET_E_INVALID;
226
227
220k
  if (!(elemp = wget_memdup(elem, size)))
228
0
    return WGET_E_MEMORY;
229
230
220k
  if ((rc = insert_element(v, elemp, v->cur, 0)) < 0)
231
0
    xfree(elemp);
232
233
220k
  return rc;
234
220k
}
235
236
/**
237
 * \param[in] v Vector where \p elem is appended to
238
 * \param[in] elem Element to append to a \p v
239
 * \return Index of inserted element (>= 0) or WGET_E_*  on error (< 0)
240
 *
241
 * Append \p elem to vector \p v.
242
 *
243
 * \p elem is *not* cloned, the vector takes 'ownership' of the element.
244
 *
245
 * An error is returned if \p v is %NULL.
246
 */
247
int wget_vector_add(wget_vector *v, const void *elem)
248
605k
{
249
605k
  return v ? insert_element(v, elem, v->cur, 0) : WGET_E_INVALID;
250
605k
}
251
252
/**
253
 * \param[in] v Vector where \p s is appended to
254
 * \param[in] fmt Printf-like format string
255
 * \param[in] args Arguments for the \p fmt
256
 * \return Index of appended element (>= 0) or WGET_E_* on error (< 0)
257
 *
258
 * Construct string in a printf-like manner and append it as an element to vector \p v.
259
 *
260
 * An error is returned if \p v or \p fmt is %NULL.
261
 */
262
int wget_vector_add_vprintf(wget_vector *v, const char *fmt, va_list args)
263
0
{
264
0
  if (!v || !fmt)
265
0
    return WGET_E_INVALID;
266
267
0
  char *p = wget_vaprintf(fmt, args);
268
0
  if (!p)
269
0
    return WGET_E_MEMORY;
270
271
0
  return insert_element(v, p, v->cur, 0);
272
0
}
273
274
/**
275
 * \param[in] v Vector where \p s is appended to
276
 * \param[in] fmt Printf-like format string
277
 * \param[in] ... Arguments for the \p fmt
278
 * \return Index of appended element (>= 0) or WGET_E_* on error (< 0)
279
 *
280
 * Construct string in a printf-like manner and append it as an element to vector \p v.
281
 *
282
 * An error is returned if \p v or \p fmt is %NULL.
283
 */
284
int wget_vector_add_printf(wget_vector *v, const char *fmt, ...)
285
0
{
286
0
  if (!v || !fmt)
287
0
    return WGET_E_INVALID;
288
289
0
  va_list args;
290
291
0
  va_start(args, fmt);
292
0
  char *p = wget_vaprintf(fmt, args);
293
0
  va_end(args);
294
295
0
  if (!p)
296
0
    return WGET_E_MEMORY;
297
298
0
  return insert_element(v, p, v->cur, 0);
299
0
}
300
301
/**
302
 * \param[in] v Vector where \p elem is inserted
303
 * \param[in] elem Element to insert into \p v
304
 * \param[in] pos Position to insert \p elem at
305
 * \return Index of inserted element (same as \p pos) (>= 0) or WGET_E_* on error (< 0)
306
 *
307
 * Replace the element at position \p pos with \p elem.
308
 * If the vector has an element destructor function, this is called.
309
 * The old element is free'd.
310
 *
311
 * \p elem is *not* cloned, the vector takes 'ownership' of the element.
312
 *
313
 * An error is returned if \p v is %NULL or \p pos is out of range (< 0 or > # of entries).
314
 */
315
int wget_vector_replace(wget_vector *v, const void *elem, int pos)
316
17.9k
{
317
17.9k
  if (!v || pos < 0 || pos >= v->cur)
318
0
    return WGET_E_INVALID;
319
320
17.9k
  if (v->destructor)
321
17.9k
    v->destructor(v->entry[pos]);
322
323
17.9k
  return insert_element(v, elem, pos, 1); // replace existing entry
324
17.9k
}
325
326
static int remove_element(wget_vector *v, int pos, int free_entry)
327
3.76k
{
328
3.76k
  if (pos < 0 || !v || pos >= v->cur)
329
0
    return WGET_E_INVALID;
330
331
3.76k
  if (free_entry) {
332
0
    if (v->destructor)
333
0
      v->destructor(v->entry[pos]);
334
0
  }
335
336
3.76k
  memmove(&v->entry[pos], &v->entry[pos + 1], (v->cur - pos - 1) * sizeof(void *));
337
3.76k
  v->cur--;
338
339
3.76k
  return pos;
340
3.76k
}
341
342
/**
343
 * \param[in] v Vector to remove an element from
344
 * \param[in] pos Position of element to remove
345
 * \return Index of appended element (>= 0) or WGET_E_* on error (< 0)
346
 *
347
 * Remove the element at position \p pos.
348
 * If the vector has an element destructor function, this is called.
349
 * The element is free'd.
350
 *
351
 * An error is returned if \p v is %NULL or \p pos is out of range (< 0 or > # of entries).
352
 */
353
int wget_vector_remove(wget_vector *v, int pos)
354
0
{
355
0
  return remove_element(v, pos, 1);
356
0
}
357
358
/**
359
 * \param[in] v Vector to remove an element from
360
 * \param[in] pos Position of element to remove
361
 * \return Index of removed element (same as \p pos) (>= 0) or WGET_E_* on error (< 0)
362
 *
363
 * Remove the element at position \p pos.
364
 * No element destructor function is called, the element is not free'd.
365
 *
366
 * An error is returned if \p v is %NULL or \p pos is out of range (< 0 or > # of entries).
367
 */
368
int wget_vector_remove_nofree(wget_vector *v, int pos)
369
3.76k
{
370
3.76k
  return remove_element(v, pos, 0);
371
3.76k
}
372
373
/**
374
 * \param[in] v Vector to act on
375
 * \param[in] old_pos Position to move element from
376
 * \param[in] new_pos Position to move element to
377
 * \return Index of new position (same as \p new_pos) (>= 0) or WGET_E_* on error (< 0)
378
 *
379
 * Move the element at position \p old_pos to \p new_pos.
380
 *
381
 * Other elements may change the position.
382
 *
383
 * An error is returned if \p v is %NULL or either \p old_pos or
384
 * \p new_pos is out of range (< 0 or > # of entries).
385
 */
386
int wget_vector_move(wget_vector *v, int old_pos, int new_pos)
387
0
{
388
0
  if (!v) return WGET_E_INVALID;
389
0
  if (old_pos < 0 || old_pos >= v->cur) return WGET_E_INVALID;
390
0
  if (new_pos < 0 || new_pos >= v->cur) return WGET_E_INVALID;
391
0
  if (old_pos == new_pos) return new_pos;
392
393
0
  if (v->sorted && v->cmp && v->cmp(v->entry[old_pos], v->entry[new_pos]))
394
0
    v->sorted = 0;
395
396
0
  void *tmp = v->entry[old_pos];
397
0
  if (old_pos < new_pos)
398
0
    memmove(&v->entry[old_pos], &v->entry[old_pos + 1], (new_pos - old_pos) * sizeof(void *));
399
0
  else
400
0
    memmove(&v->entry[new_pos + 1], &v->entry[new_pos], (old_pos - new_pos) * sizeof(void *));
401
0
  v->entry[new_pos] = tmp;
402
403
0
  return new_pos;
404
0
}
405
406
/**
407
 * \param[in] v Vector to act on
408
 * \param[in] pos1 Position of element one
409
 * \param[in] pos2 Position of element two
410
 * \return Index of second position (same as \p pos2) (>= 0) or WGET_E_*  on error (< 0)
411
 *
412
 * Swap the two elements at position \p pos1 and \p pos2.
413
 *
414
 * An error is returned if \p v is %NULL or either \p pos1 or
415
 * \p pos2 is out of range (< 0 or > # of entries).
416
 */
417
int wget_vector_swap(wget_vector *v, int pos1, int pos2)
418
0
{
419
0
  if (!v) return WGET_E_INVALID;
420
0
  if (pos1 < 0 || pos1 >= v->cur) return WGET_E_INVALID;
421
0
  if (pos2 < 0 || pos2 >= v->cur) return WGET_E_INVALID;
422
0
  if (pos1 == pos2) return pos2;
423
424
0
  void *tmp = v->entry[pos1];
425
0
  v->entry[pos1] = v->entry[pos2];
426
0
  v->entry[pos2] = tmp;
427
428
0
  if (v->sorted && v->cmp && v->cmp(v->entry[pos1], v->entry[pos2]))
429
0
    v->sorted = 0;
430
431
0
  return pos2;
432
0
}
433
434
/**
435
 * \param[in] v Vector to be free'd
436
 *
437
 * Free the vector \p v and it's contents.
438
 *
439
 * For each element the destructor function is called and the element free'd thereafter.
440
 * Then the vector itself is free'd and set to %NULL.
441
 */
442
void wget_vector_free(wget_vector **v)
443
238k
{
444
238k
  if (v && *v) {
445
53.2k
    wget_vector_clear(*v);
446
53.2k
    xfree((*v)->entry);
447
53.2k
    xfree(*v);
448
53.2k
  }
449
238k
}
450
451
/**
452
 * \param[in] v Vector to be cleared
453
 *
454
 * Free all elements of the vector \p v but not the vector itself.
455
 *
456
 * For each element the destructor function is called and the element free'd thereafter.
457
 * The vector is then empty and can be reused.
458
 */
459
void wget_vector_clear(wget_vector *v)
460
54.3k
{
461
54.3k
  if (v) {
462
54.0k
    if (v->destructor) {
463
879k
      for (int it = 0; it < v->cur; it++) {
464
825k
        v->destructor(v->entry[it]);
465
825k
        v->entry[it] = NULL;
466
825k
      }
467
54.0k
    }
468
469
54.0k
    v->cur = 0;
470
54.0k
  }
471
54.3k
}
472
473
/**
474
 * \param[in] v Vector to be cleared
475
 *
476
 * Remove all elements of the vector \p v without free'ing them.
477
 * The caller is responsible to care for the elements.
478
 *
479
 * The vector is then empty and can be reused.
480
 */
481
void wget_vector_clear_nofree(wget_vector *v)
482
6.83k
{
483
6.83k
  if (v) {
484
9.08k
    for (int it = 0; it < v->cur; it++)
485
2.65k
      v->entry[it] = NULL;
486
6.42k
    v->cur = 0;
487
6.42k
  }
488
6.83k
}
489
490
/**
491
 * \param[in] v Vector
492
 * \return The number of elements in the vector \p v
493
 *
494
 * Retrieve the number of elements of the vector \p v.
495
 * If \p v is %NULL, 0 is returned.
496
 */
497
int wget_vector_size(const wget_vector *v)
498
202k
{
499
202k
  return v ? v->cur : 0;
500
202k
}
501
502
/**
503
 * \param[in] v Vector
504
 * \param[in] pos Position of element to retrieve
505
 * \return The element at position \p pos or %NULL on error
506
 *
507
 * Retrieve the element at position \p pos.
508
 *
509
 * %NULL is returned if \p v is %NULL or \p pos is out of range (< 0 or > # of entries).
510
 */
511
void *wget_vector_get(const wget_vector *v, int pos)
512
80.6k
{
513
80.6k
  if (pos < 0 || !v || pos >= v->cur)
514
1.66k
    return NULL;
515
516
78.9k
  return v->entry[pos];
517
80.6k
}
518
519
/**
520
 * \param[in] v Vector
521
 * \param[in] browse Function to be called for each element of \p v
522
 * \param[in] ctx Context variable use as param to \p browse
523
 * \return Return value of the last call to \p browse
524
 *
525
 * Call function \p browse for each element of vector \p v or until \p browse
526
 * returns a value not equal to zero.
527
 *
528
 * \p browse is called with \p ctx and the pointer to the current element.
529
 *
530
 * The return value of the last call to \p browse is returned or 0 if \p v is %NULL.
531
 */
532
int wget_vector_browse(const wget_vector *v, wget_vector_browse_fn *browse, void *ctx)
533
0
{
534
0
  if (v) {
535
0
    for (int ret, it = 0; it < v->cur; it++)
536
0
      if ((ret = browse(ctx, v->entry[it])) != 0)
537
0
        return ret;
538
0
  }
539
540
0
  return 0;
541
0
}
542
543
/**
544
 * \param[in] v Vector
545
 * \param[in] cmp Function to compare elements
546
 *
547
 * Set the compare function used by wget_vector_sort().
548
 */
549
void wget_vector_setcmpfunc(wget_vector *v, wget_vector_compare_fn *cmp)
550
6.25k
{
551
6.25k
  if (v) {
552
3.93k
    v->cmp = cmp;
553
554
3.93k
    if (v->cur == 1)
555
2.87k
      v->sorted = 1;
556
1.05k
    else
557
1.05k
      v->sorted = 0;
558
3.93k
  }
559
6.25k
}
560
561
/**
562
 * \param[in] v Vector
563
 * \param[in] destructor Function to be called for element destruction
564
 *
565
 * Set the destructor function that is called for each element to be removed.
566
 * It should not free the element (pointer) itself.
567
 */
568
void wget_vector_set_destructor(wget_vector *v, wget_vector_destructor *destructor)
569
33.6k
{
570
33.6k
  if (v)
571
33.6k
    v->destructor = destructor;
572
33.6k
}
573
574
WGET_GCC_NONNULL_ALL
575
static int compare_element(const void *p1, const void *p2, void *v)
576
81.1k
{
577
81.1k
  return ((wget_vector *)v)->cmp(*((void **)p1), *((void **)p2));
578
81.1k
}
579
580
/**
581
 * \param[in] v Vector
582
 *
583
 * Sort the elements in vector \p v using the compare function.
584
 * Do nothing if \p v is %NULL or the compare function is not set.
585
 */
586
void wget_vector_sort(wget_vector *v)
587
10.0k
{
588
10.0k
  if (v && v->cmp) {
589
7.30k
    qsort_r(v->entry, v->cur, sizeof(void *), compare_element, v);
590
7.30k
    v->sorted = 1;
591
7.30k
  }
592
10.0k
}
593
594
/**
595
 * \param[in] v Vector
596
 * \param[in] elem Element to search for
597
 * \return Index of the found element, WGET_E_UNKNOWN if not found or WGET_E_INVALID
598
 *         if v was %NULL or there was no comparison function set
599
 *
600
 * Searches for the given element using the compare function of the vector.
601
 */
602
int wget_vector_find(const wget_vector *v, const void *elem)
603
11.5k
{
604
11.5k
  if (!v || !v->cmp)
605
0
    return WGET_E_INVALID;
606
607
11.5k
  if (v->cur == 1) {
608
2.73k
    if (v->cmp(elem, v->entry[0]) == 0) return 0;
609
8.80k
  } else if (v->sorted) {
610
    // binary search for element (exact match)
611
27.2k
    for (int l = 0, r = v->cur - 1; l <= r;) {
612
23.4k
      int res, m = (l + r) / 2;
613
23.4k
      if ((res = v->cmp(elem, v->entry[m])) > 0) l = m + 1;
614
7.18k
      else if (res < 0) r = m - 1;
615
2.55k
      else return m;
616
23.4k
    }
617
6.38k
  } else {
618
    // linear search for element
619
3.06k
    for (int it = 0; it < v->cur; it++)
620
656
      if (v->cmp(elem, v->entry[it]) == 0) return it;
621
2.41k
  }
622
623
7.04k
  return WGET_E_UNKNOWN; // not found
624
11.5k
}
625
626
/**
627
 * \param[in] v Vector
628
 * \param[in] elem Element to check for
629
 * \return True if element exists, else false
630
 *
631
 * Checks whether the element \p elem exists or not.
632
 */
633
bool wget_vector_contains(const wget_vector *v, const void *elem)
634
0
{
635
0
  return wget_vector_find(v, elem) >= 0;
636
0
}
637
638
/**
639
 * \param[in] v Vector
640
 * \param[in] start Index to start search from
641
 * \param[in] direction Direction of search
642
 * \param[in] find Function to be called for each element
643
 * \return Index of the found element, WGET_E_UNKNOWN if not found or WGET_E_INVALID
644
 *         if v was %NULL or there was no comparison function set
645
 *
646
 * Call \p find for each element starting at \p start.
647
 * If \p find returns 0 the current index is returned.
648
 */
649
int wget_vector_findext(const wget_vector *v, int start, int direction, wget_vector_find_fn *find)
650
0
{
651
0
  if (!v)
652
0
    return WGET_E_INVALID;
653
654
0
  if (direction) { // down
655
0
    if (start < v->cur) {
656
0
      for (int it = start; it >= 0; it--)
657
0
        if (find(v->entry[it]) == 0)
658
0
          return it;
659
0
    }
660
0
  } else { // up
661
0
    if (start >= 0) {
662
0
      for (int it = start; it < v->cur; it++)
663
0
        if (find(v->entry[it]) == 0)
664
0
          return it;
665
0
    }
666
0
  }
667
668
0
  return WGET_E_UNKNOWN;
669
0
}
670
671
/**@}*/