Coverage Report

Created: 2024-01-26 01:52

/work/toxcore/list.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: GPL-3.0-or-later
2
 * Copyright © 2016-2018 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 <stdlib.h>
16
#include <string.h>
17
18
#include "ccompat.h"
19
20
/**
21
 * Basically, the elements in the list are placed in order so that they can be searched for easily
22
 * - each element is seen as a big-endian integer when ordering them
23
 * - the ids array is maintained so that each id always matches
24
 * - the search algorithm cuts down the time to find the id associated with a piece of data
25
 *   at the cost of slow add/remove functions for large lists
26
 * - Starts at `1/2` of the array, compares the element in the array with the data,
27
 *   then moves `+/- 1/4` of the array depending on whether the value is greater or lower,
28
 *   then `+- 1/8`, etc, until the value is matched or its position where it should be in the array is found
29
 * - some considerations since the array size is never perfect
30
 */
31
32
static int32_t
33
list_index(uint32_t i)
34
6.58k
{
35
6.58k
    return ~i;
36
6.58k
}
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
non_null()
45
static int find(const BS_List *list, const uint8_t *data)
46
221k
{
47
    // should work well, but could be improved
48
221k
    if (list->n == 0) {
49
1.81k
        return list_index(0);
50
1.81k
    }
51
52
220k
    uint32_t i = list->n / 2; // current position in the array
53
220k
    uint32_t delta = i / 2;   // how much we move in the array
54
55
220k
    if (delta == 0) {
56
139k
        delta = 1;
57
139k
    }
58
59
220k
    int d = -1; // used to determine if closest match is found
60
    // closest match is found if we move back to where we have already been
61
62
339k
    while (true) {
63
339k
        const int r = memcmp(data, list->data + list->element_size * i, list->element_size);
64
65
339k
        if (r == 0) {
66
215k
            return i;
67
215k
        }
68
69
124k
        if (r > 0) {
70
            // data is greater
71
            // move down
72
43.8k
            i += delta;
73
74
43.8k
            if (d == 0 || i == list->n) {
75
                // reached bottom of list, or closest match
76
891
                return list_index(i);
77
891
            }
78
79
42.9k
            delta = delta / 2;
80
81
42.9k
            if (delta == 0) {
82
42.2k
                delta = 1;
83
42.2k
                d = 1;
84
42.2k
            }
85
80.7k
        } else {
86
            // data is smaller
87
80.7k
            if (d == 1 || i == 0) {
88
                // reached top or list or closest match
89
3.87k
                return list_index(i);
90
3.87k
            }
91
92
            // move up
93
76.8k
            i -= delta;
94
95
76.8k
            delta = delta / 2;
96
97
76.8k
            if (delta == 0) {
98
75.9k
                delta = 1;
99
75.9k
                d = 0;
100
75.9k
            }
101
76.8k
        }
102
124k
    }
103
220k
}
104
105
/**
106
 * Resizes the list.
107
 *
108
 * @return true on success.
109
 */
110
non_null()
111
static bool resize(BS_List *list, uint32_t new_size)
112
4.60k
{
113
4.60k
    if (new_size == 0) {
114
0
        bs_list_free(list);
115
0
        return true;
116
0
    }
117
118
4.60k
    uint8_t *data = (uint8_t *)realloc(list->data, list->element_size * new_size);
119
120
4.60k
    if (data == nullptr) {
121
30
        return false;
122
30
    }
123
124
4.57k
    list->data = data;
125
126
4.57k
    int *ids = (int *)realloc(list->ids, new_size * sizeof(int));
127
128
4.57k
    if (ids == nullptr) {
129
30
        return false;
130
30
    }
131
132
4.54k
    list->ids = ids;
133
134
4.54k
    return true;
135
4.57k
}
136
137
138
int bs_list_init(BS_List *list, uint32_t element_size, uint32_t initial_capacity)
139
3.89k
{
140
    // set initial values
141
3.89k
    list->n = 0;
142
3.89k
    list->element_size = element_size;
143
3.89k
    list->capacity = 0;
144
3.89k
    list->data = nullptr;
145
3.89k
    list->ids = nullptr;
146
147
3.89k
    if (initial_capacity != 0) {
148
3.89k
        if (!resize(list, initial_capacity)) {
149
40
            return 0;
150
40
        }
151
3.89k
    }
152
153
3.85k
    list->capacity = initial_capacity;
154
155
3.85k
    return 1;
156
3.89k
}
157
158
void bs_list_free(BS_List *list)
159
2.84k
{
160
2.84k
    if (list == nullptr) {
161
0
        return;
162
0
    }
163
164
    // free both arrays
165
2.84k
    free(list->data);
166
2.84k
    list->data = nullptr;
167
168
2.84k
    free(list->ids);
169
2.84k
    list->ids = nullptr;
170
2.84k
}
171
172
int bs_list_find(const BS_List *list, const uint8_t *data)
173
215k
{
174
215k
    const int r = find(list, data);
175
176
    // return only -1 and positive values
177
215k
    if (r < 0) {
178
1.17k
        return -1;
179
1.17k
    }
180
181
213k
    return list->ids[r];
182
215k
}
183
184
bool bs_list_add(BS_List *list, const uint8_t *data, int id)
185
1.84k
{
186
    // find where the new element should be inserted
187
    // see: return value of find()
188
1.84k
    int i = find(list, data);
189
190
1.84k
    if (i >= 0) {
191
        // already in list
192
0
        return false;
193
0
    }
194
195
1.84k
    i = ~i;
196
197
    // increase the size of the arrays if needed
198
1.84k
    if (list->n == list->capacity) {
199
        // 1.5 * n + 1
200
21
        const uint32_t new_capacity = list->n + list->n / 2 + 1;
201
202
21
        if (!resize(list, new_capacity)) {
203
0
            return false;
204
0
        }
205
206
21
        list->capacity = new_capacity;
207
21
    }
208
209
    // insert data to element array
210
1.84k
    assert(list->data != nullptr);
211
1.84k
    memmove(list->data + (i + 1) * list->element_size, list->data + i * list->element_size,
212
1.84k
            (list->n - i) * list->element_size);
213
1.84k
    memcpy(list->data + i * list->element_size, data, list->element_size);
214
215
    // insert id to id array
216
1.84k
    memmove(&list->ids[i + 1], &list->ids[i], (list->n - i) * sizeof(int));
217
1.84k
    list->ids[i] = id;
218
219
    // increase n
220
1.84k
    ++list->n;
221
222
1.84k
    return true;
223
1.84k
}
224
225
bool bs_list_remove(BS_List *list, const uint8_t *data, int id)
226
4.93k
{
227
4.93k
    const int i = find(list, data);
228
229
4.93k
    if (i < 0) {
230
3.56k
        return false;
231
3.56k
    }
232
233
1.37k
    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.37k
    if (list->n < list->capacity / 2) {
240
691
        const uint32_t new_capacity = list->capacity / 2;
241
242
691
        if (resize(list, new_capacity)) {
243
671
            list->capacity = new_capacity;
244
671
        }
245
691
    }
246
247
1.37k
    --list->n;
248
249
1.37k
    memmove(list->data + i * list->element_size, list->data + (i + 1) * list->element_size,
250
1.37k
            (list->n - i) * list->element_size);
251
1.37k
    memmove(&list->ids[i], &list->ids[i + 1], (list->n - i) * sizeof(int));
252
253
1.37k
    return true;
254
1.37k
}