Coverage Report

Created: 2026-10-24 08:16

/work/toxcore/list.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: GPL-3.0-or-later
2
 * Copyright © 2016-2026 The TokTok team.
3
 * Copyright © 2014 Tox project.
4
 */
5
6
/**
7
 * Simple struct with functions to create a list which associates ids with data
8
 * - Allows for finding ids associated with data such as IPs or public keys in a short time
9
 * - Should only be used if there are relatively few add/remove calls to the list
10
 */
11
#include "list.h"
12
13
#include <assert.h>
14
#include <stdbool.h>
15
#include <string.h>
16
17
#include "attributes.h"
18
#include "ccompat.h"
19
#include "mem.h"
20
21
/**
22
 * Basically, the elements in the list are placed in order so that they can be searched for easily
23
 * - each element is seen as a big-endian integer when ordering them
24
 * - the ids array is maintained so that each id always matches
25
 * - the search algorithm cuts down the time to find the id associated with a piece of data
26
 *   at the cost of slow add/remove functions for large lists
27
 * - Starts at `1/2` of the array, compares the element in the array with the data,
28
 *   then moves `+/- 1/4` of the array depending on whether the value is greater or lower,
29
 *   then `+- 1/8`, etc, until the value is matched or its position where it should be in the array is found
30
 * - some considerations since the array size is never perfect
31
 */
32
33
static int32_t list_index(uint32_t i)
34
3.84k
{
35
3.84k
    return ~i;
36
3.84k
}
37
38
/** @brief Find data in list
39
 *
40
 * @retval >=0 index of data in array
41
 * @retval <0  no match, returns index (return value is `list_index(index)`) where
42
 *   the data should be inserted
43
 */
44
static int find(const BS_List *_Nonnull list, const uint8_t *_Nonnull data)
45
100k
{
46
    // should work well, but could be improved
47
100k
    if (list->n == 0) {
48
1.71k
        return list_index(0);
49
1.71k
    }
50
51
98.8k
    uint32_t i = list->n / 2; // current position in the array
52
98.8k
    uint32_t delta = i / 2;   // how much we move in the array
53
54
98.8k
    if (delta == 0) {
55
94.0k
        delta = 1;
56
94.0k
    }
57
58
98.8k
    int d = -1; // used to determine if closest match is found
59
    // closest match is found if we move back to where we have already been
60
61
150k
    while (true) {
62
150k
        const int r = list->cmp_callback(data, list->data + list->element_size * i, list->element_size);
63
64
150k
        if (r == 0) {
65
96.7k
            return i;
66
96.7k
        }
67
68
53.5k
        if (r > 0) {
69
            // data is greater
70
            // move down
71
4.49k
            i += delta;
72
73
4.49k
            if (d == 0 || i == list->n) {
74
                // reached bottom of list, or closest match
75
786
                return list_index(i);
76
786
            }
77
78
3.71k
            delta = delta / 2;
79
80
3.71k
            if (delta == 0) {
81
2.65k
                delta = 1;
82
2.65k
                d = 1;
83
2.65k
            }
84
49.0k
        } else {
85
            // data is smaller
86
49.0k
            if (d == 1 || i == 0) {
87
                // reached top or list or closest match
88
1.34k
                return list_index(i);
89
1.34k
            }
90
91
            // move up
92
47.7k
            i -= delta;
93
94
47.7k
            delta = delta / 2;
95
96
47.7k
            if (delta == 0) {
97
46.3k
                delta = 1;
98
46.3k
                d = 0;
99
46.3k
            }
100
47.7k
        }
101
53.5k
    }
102
98.8k
}
103
104
/**
105
 * Resizes the list.
106
 *
107
 * @return true on success.
108
 */
109
static bool resize(BS_List *_Nonnull list, uint32_t new_size)
110
3.24k
{
111
3.24k
    if (new_size == 0) {
112
0
        bs_list_free(list);
113
0
        return true;
114
0
    }
115
116
3.24k
    uint8_t *data = (uint8_t *)mem_brealloc(list->mem, list->data, new_size * list->element_size);
117
118
3.24k
    if (data == nullptr) {
119
58
        return false;
120
58
    }
121
122
3.18k
    list->data = data;
123
124
3.18k
    int *ids = (int *)mem_vrealloc(list->mem, list->ids, new_size, sizeof(int));
125
126
3.18k
    if (ids == nullptr) {
127
3
        return false;
128
3
    }
129
130
3.18k
    list->ids = ids;
131
132
3.18k
    return true;
133
3.18k
}
134
135
int bs_list_init(BS_List *list, const Memory *mem, uint32_t element_size, uint32_t initial_capacity, bs_list_cmp_cb *cmp_callback)
136
3.92k
{
137
3.92k
    list->mem = mem;
138
139
    // set initial values
140
3.92k
    list->n = 0;
141
3.92k
    list->element_size = element_size;
142
3.92k
    list->capacity = 0;
143
3.92k
    list->data = nullptr;
144
3.92k
    list->ids = nullptr;
145
3.92k
    list->cmp_callback = cmp_callback;
146
147
3.92k
    if (initial_capacity != 0) {
148
2.00k
        if (!resize(list, initial_capacity)) {
149
61
            return 0;
150
61
        }
151
2.00k
    }
152
153
3.86k
    list->capacity = initial_capacity;
154
155
3.86k
    return 1;
156
3.92k
}
157
158
void bs_list_free(BS_List *list)
159
3.92k
{
160
3.92k
    if (list == nullptr) {
161
0
        return;
162
0
    }
163
164
    // free both arrays
165
3.92k
    mem_delete(list->mem, list->data);
166
3.92k
    list->data = nullptr;
167
168
3.92k
    mem_delete(list->mem, list->ids);
169
3.92k
    list->ids = nullptr;
170
3.92k
}
171
172
int bs_list_find(const BS_List *list, const uint8_t *data)
173
96.5k
{
174
96.5k
    const int r = find(list, data);
175
176
    // return only -1 and positive values
177
96.5k
    if (r < 0) {
178
1.21k
        return -1;
179
1.21k
    }
180
181
95.2k
    return list->ids[r];
182
96.5k
}
183
184
bool bs_list_add(BS_List *list, const uint8_t *data, int id)
185
1.48k
{
186
    // find where the new element should be inserted
187
    // see: return value of find()
188
1.48k
    int i = find(list, data);
189
190
1.48k
    if (i >= 0) {
191
        // already in list
192
0
        return false;
193
0
    }
194
195
1.48k
    i = ~i;
196
197
    // increase the size of the arrays if needed
198
1.48k
    if (list->n == list->capacity) {
199
        // 1.5 * n + 1
200
825
        const uint32_t new_capacity = list->n + list->n / 2 + 1;
201
202
825
        if (!resize(list, new_capacity)) {
203
0
            return false;
204
0
        }
205
206
825
        list->capacity = new_capacity;
207
825
    }
208
209
    // insert data to element array
210
1.48k
    assert(list->data != nullptr);
211
1.48k
    memmove(list->data + (i + 1) * list->element_size, list->data + i * list->element_size,
212
1.48k
            (list->n - i) * list->element_size);
213
1.48k
    memcpy(list->data + i * list->element_size, data, list->element_size);
214
215
    // insert id to id array
216
1.48k
    memmove(&list->ids[i + 1], &list->ids[i], (list->n - i) * sizeof(int));
217
1.48k
    list->ids[i] = id;
218
219
    // increase n
220
1.48k
    ++list->n;
221
222
1.48k
    return true;
223
1.48k
}
224
225
bool bs_list_remove(BS_List *list, const uint8_t *data, int id)
226
2.56k
{
227
2.56k
    const int i = find(list, data);
228
229
2.56k
    if (i < 0) {
230
1.14k
        return false;
231
1.14k
    }
232
233
1.41k
    if (list->ids[i] != id) {
234
        // this should never happen
235
0
        return false;
236
0
    }
237
238
    // decrease the size of the arrays if needed
239
1.41k
    if (list->n < list->capacity / 2) {
240
418
        const uint32_t new_capacity = list->capacity / 2;
241
242
418
        if (resize(list, new_capacity)) {
243
418
            list->capacity = new_capacity;
244
418
        }
245
418
    }
246
247
1.41k
    --list->n;
248
249
1.41k
    memmove(list->data + i * list->element_size, list->data + (i + 1) * list->element_size,
250
1.41k
            (list->n - i) * list->element_size);
251
1.41k
    memmove(&list->ids[i], &list->ids[i + 1], (list->n - i) * sizeof(int));
252
253
1.41k
    return true;
254
1.41k
}